Merge branch 'develop'
This commit is contained in:
commit
1c008b585d
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
|
||||
- name: Generate project files
|
||||
run: tools/premake5 vs2022 --ac-disable
|
||||
run: tools/premake5 vs2022
|
||||
|
||||
- name: Set up problem matching
|
||||
uses: ammaraskar/msvc-problem-matcher@master
|
||||
|
32
CHANGELOG.md
32
CHANGELOG.md
@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [0.7.1] - 2022-05-03
|
||||
|
||||
### Added
|
||||
|
||||
- Add ToUpper GSC Function (#216)
|
||||
- Add StrICmp GSC Function (#216)
|
||||
- Add IsEndStr GSC Function (#216)
|
||||
- Add DropAllBots GSC Function (#174)
|
||||
- Add GSC entity field `entityflags` (#228)
|
||||
- Add GSC client field `clientflags` (#228)
|
||||
- Add bg_surfacePenetration Dvar (#241)
|
||||
- Add bg_bulletRange Dvar (#241)
|
||||
|
||||
### Changed
|
||||
|
||||
- Test clients' native functionality has been restored by default (#162)
|
||||
- Custom GSC functions can be called correctly from a game script (#216)
|
||||
- Master server list will be used instead of the node system (load server list faster) (#234)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed issue with mouse acceleration when polling rate is greater than 125Hz (#230)
|
||||
- Fixed issue with player speed caused by sprinting from the prone position (#232)
|
||||
- Fixed client crash when cg_chatHeight was set to 0 (#237)
|
||||
- Fixed GSC function Scr_TableLookupIStringByRow (#162)
|
||||
|
||||
### Known issue
|
||||
|
||||
- HTTPS is not supported for fast downloads at the moment.
|
||||
- Sound issue fix is experimental as the bug is not fully understood.
|
||||
- `reloadmenus` command does not free resources used by custom menus.
|
||||
|
||||
## [0.7.0] - 2022-05-01
|
||||
|
||||
### Added
|
||||
|
@ -19,15 +19,9 @@
|
||||
|:----------------------------|:-----------------------------------------------|
|
||||
| `--copy-to=PATH` | Optional, copy the DLL to a custom folder after build, define the path here if wanted. |
|
||||
| `--copy-pdb` | Copy debug information for binaries as well to the path given via --copy-to. |
|
||||
| `--ac-disable` | Disable anticheat. |
|
||||
| `--ac-debug-detections` | Log anticheat detections. |
|
||||
| `--ac-debug-load-library` | Log libraries that get loaded. |
|
||||
| `--force-unit-tests` | Always compile unit tests. |
|
||||
| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. |
|
||||
| `--force-minidump-upload` | Upload minidumps even for Debug builds. |
|
||||
| `--disable-bitmessage` | Disable use of BitMessage completely. |
|
||||
| `--disable-base128` | Disable base128 encoding for minidumps. |
|
||||
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
|
||||
| `--iw4x-zones` | Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches. |
|
||||
|
||||
## Command line arguments
|
||||
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
||||
Subproject commit 5108f12350b6daa4aa5dbc846517ad1db2f8388a
|
||||
Subproject commit 4b47368501321c795d5b54d87a5bab35a21a7940
|
@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
echo Updating submodules...
|
||||
call git submodule update --init --recursive
|
||||
call tools\premake5 %* vs2022 --ac-disable
|
||||
call tools\premake5 %* vs2022
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,102 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__APPLE__) || defined(__unix__)
|
||||
#define VMP_IMPORT
|
||||
#define VMP_API
|
||||
#define VMP_WCHAR unsigned short
|
||||
#else
|
||||
#define VMP_IMPORT __declspec(dllimport)
|
||||
#define VMP_API __stdcall
|
||||
#define VMP_WCHAR wchar_t
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "VMProtectSDK64.lib")
|
||||
#else
|
||||
#pragma comment(lib, "VMProtectSDK32.lib")
|
||||
#endif // _WIN64
|
||||
#endif // __APPLE__ || __unix__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// protection
|
||||
VMP_IMPORT void VMP_API VMProtectBegin(const char *);
|
||||
VMP_IMPORT void VMP_API VMProtectBeginVirtualization(const char *);
|
||||
VMP_IMPORT void VMP_API VMProtectBeginMutation(const char *);
|
||||
VMP_IMPORT void VMP_API VMProtectBeginUltra(const char *);
|
||||
VMP_IMPORT void VMP_API VMProtectBeginVirtualizationLockByKey(const char *);
|
||||
VMP_IMPORT void VMP_API VMProtectBeginUltraLockByKey(const char *);
|
||||
VMP_IMPORT void VMP_API VMProtectEnd(void);
|
||||
|
||||
// utils
|
||||
VMP_IMPORT bool VMP_API VMProtectIsProtected();
|
||||
VMP_IMPORT bool VMP_API VMProtectIsDebuggerPresent(bool);
|
||||
VMP_IMPORT bool VMP_API VMProtectIsVirtualMachinePresent(void);
|
||||
VMP_IMPORT bool VMP_API VMProtectIsValidImageCRC(void);
|
||||
VMP_IMPORT const char * VMP_API VMProtectDecryptStringA(const char *value);
|
||||
VMP_IMPORT const VMP_WCHAR * VMP_API VMProtectDecryptStringW(const VMP_WCHAR *value);
|
||||
VMP_IMPORT bool VMP_API VMProtectFreeString(const void *value);
|
||||
|
||||
// licensing
|
||||
enum VMProtectSerialStateFlags
|
||||
{
|
||||
SERIAL_STATE_SUCCESS = 0,
|
||||
SERIAL_STATE_FLAG_CORRUPTED = 0x00000001,
|
||||
SERIAL_STATE_FLAG_INVALID = 0x00000002,
|
||||
SERIAL_STATE_FLAG_BLACKLISTED = 0x00000004,
|
||||
SERIAL_STATE_FLAG_DATE_EXPIRED = 0x00000008,
|
||||
SERIAL_STATE_FLAG_RUNNING_TIME_OVER = 0x00000010,
|
||||
SERIAL_STATE_FLAG_BAD_HWID = 0x00000020,
|
||||
SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED = 0x00000040,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct
|
||||
{
|
||||
unsigned short wYear;
|
||||
unsigned char bMonth;
|
||||
unsigned char bDay;
|
||||
} VMProtectDate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int nState; // VMProtectSerialStateFlags
|
||||
VMP_WCHAR wUserName[256]; // user name
|
||||
VMP_WCHAR wEMail[256]; // email
|
||||
VMProtectDate dtExpire; // date of serial number expiration
|
||||
VMProtectDate dtMaxBuild; // max date of build, that will accept this key
|
||||
int bRunningTime; // running time in minutes
|
||||
unsigned char nUserDataLength; // length of user data in bUserData
|
||||
unsigned char bUserData[255]; // up to 255 bytes of user data
|
||||
} VMProtectSerialNumberData;
|
||||
#pragma pack(pop)
|
||||
|
||||
VMP_IMPORT int VMP_API VMProtectSetSerialNumber(const char *serial);
|
||||
VMP_IMPORT int VMP_API VMProtectGetSerialNumberState();
|
||||
VMP_IMPORT bool VMP_API VMProtectGetSerialNumberData(VMProtectSerialNumberData *data, int size);
|
||||
VMP_IMPORT int VMP_API VMProtectGetCurrentHWID(char *hwid, int size);
|
||||
|
||||
// activation
|
||||
enum VMProtectActivationFlags
|
||||
{
|
||||
ACTIVATION_OK = 0,
|
||||
ACTIVATION_SMALL_BUFFER,
|
||||
ACTIVATION_NO_CONNECTION,
|
||||
ACTIVATION_BAD_REPLY,
|
||||
ACTIVATION_BANNED,
|
||||
ACTIVATION_CORRUPTED,
|
||||
ACTIVATION_BAD_CODE,
|
||||
ACTIVATION_ALREADY_USED,
|
||||
ACTIVATION_SERIAL_UNKNOWN,
|
||||
ACTIVATION_EXPIRED,
|
||||
ACTIVATION_NOT_AVAILABLE
|
||||
};
|
||||
|
||||
VMP_IMPORT int VMP_API VMProtectActivateLicense(const char *code, char *serial, int size);
|
||||
VMP_IMPORT int VMP_API VMProtectDeactivateLicense(const char *serial);
|
||||
VMP_IMPORT int VMP_API VMProtectGetOfflineActivationString(const char *code, char *buf, int size);
|
||||
VMP_IMPORT int VMP_API VMProtectGetOfflineDeactivationString(const char *serial, char *buf, int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
27
premake5.lua
27
premake5.lua
@ -71,21 +71,6 @@ newoption {
|
||||
description = "Copy debug information for binaries as well to the path given via --copy-to."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "ac-disable",
|
||||
description = "Disable anticheat."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "ac-debug-detections",
|
||||
description = "Log anticheat detections."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "ac-debug-load-library",
|
||||
description = "Log libraries that get loaded."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "force-unit-tests",
|
||||
description = "Always compile unit tests."
|
||||
@ -269,23 +254,11 @@ workspace "iw4x"
|
||||
"./src",
|
||||
"./lib/include",
|
||||
}
|
||||
syslibdirs {
|
||||
"./lib/bin",
|
||||
}
|
||||
resincludedirs {
|
||||
"$(ProjectDir)src" -- fix for VS IDE
|
||||
}
|
||||
|
||||
-- Debug flags
|
||||
if _OPTIONS["ac-disable"] then
|
||||
defines {"DISABLE_ANTICHEAT"}
|
||||
end
|
||||
if _OPTIONS["ac-debug-detections"] then
|
||||
defines {"DEBUG_DETECTIONS"}
|
||||
end
|
||||
if _OPTIONS["ac-debug-load-library"] then
|
||||
defines {"DEBUG_LOAD_LIBRARY"}
|
||||
end
|
||||
if _OPTIONS["force-unit-tests"] then
|
||||
defines {"FORCE_UNIT_TESTS"}
|
||||
end
|
||||
|
@ -69,7 +69,6 @@ namespace Components
|
||||
Loader::Register(new Renderer());
|
||||
Loader::Register(new UIFeeder());
|
||||
Loader::Register(new UIScript());
|
||||
Loader::Register(new AntiCheat());
|
||||
Loader::Register(new Changelog());
|
||||
Loader::Register(new Dedicated());
|
||||
Loader::Register(new Discovery());
|
||||
@ -84,6 +83,7 @@ namespace Components
|
||||
Loader::Register(new ModelSurfs());
|
||||
Loader::Register(new PlayerName());
|
||||
Loader::Register(new QuickPatch());
|
||||
Loader::Register(new Security());
|
||||
Loader::Register(new ServerInfo());
|
||||
Loader::Register(new ServerList());
|
||||
Loader::Register(new SlowMotion());
|
||||
@ -104,8 +104,9 @@ namespace Components
|
||||
Loader::Register(new Movement());
|
||||
Loader::Register(new Elevators());
|
||||
Loader::Register(new ClientCommand());
|
||||
|
||||
Loader::Register(new Client());
|
||||
Loader::Register(new ScriptExtension());
|
||||
Loader::Register(new RawMouse());
|
||||
Loader::Register(new Bullet());
|
||||
|
||||
Loader::Pregame = false;
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ namespace Components
|
||||
#include "Modules/Network.hpp"
|
||||
#include "Modules/Theatre.hpp"
|
||||
#include "Modules/QuickPatch.hpp"
|
||||
#include "Modules/Security.hpp"
|
||||
#include "Modules/Node.hpp"
|
||||
#include "Modules/RCon.hpp"
|
||||
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
|
||||
@ -99,7 +100,6 @@ namespace Components
|
||||
#include "Modules/RawFiles.hpp"
|
||||
#include "Modules/Renderer.hpp"
|
||||
#include "Modules/UIFeeder.hpp"
|
||||
#include "Modules/AntiCheat.hpp"
|
||||
#include "Modules/Changelog.hpp"
|
||||
#include "Modules/Dedicated.hpp"
|
||||
#include "Modules/Discovery.hpp"
|
||||
@ -134,6 +134,7 @@ namespace Components
|
||||
#include "Modules/Movement.hpp"
|
||||
#include "Modules/Elevators.hpp"
|
||||
#include "Modules/ClientCommand.hpp"
|
||||
|
||||
#include "Modules/Gamepad.hpp"
|
||||
#include "Modules/Client.hpp"
|
||||
#include "Modules/ScriptExtension.hpp"
|
||||
#include "Modules/RawMouse.hpp"
|
||||
#include "Modules/Bullet.hpp"
|
||||
|
@ -1,944 +0,0 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Utils::Time::Interval AntiCheat::LastCheck;
|
||||
Utils::Hook AntiCheat::CreateThreadHook;
|
||||
Utils::Hook AntiCheat::LoadLibHook[6];
|
||||
Utils::Hook AntiCheat::VirtualProtectHook[2];
|
||||
unsigned long AntiCheat::Flags = NO_FLAG;
|
||||
|
||||
std::mutex AntiCheat::ThreadMutex;
|
||||
std::vector<DWORD> AntiCheat::OwnThreadIds;
|
||||
std::map<DWORD, std::shared_ptr<Utils::Hook>> AntiCheat::ThreadHookMap;
|
||||
|
||||
// This function does nothing, it only adds the two passed variables and returns the value
|
||||
// The only important thing it does is to clean the first parameter, and then return
|
||||
// By returning, the crash procedure will be called, as it hasn't been cleaned from the stack
|
||||
__declspec(naked) void AntiCheat::NullSub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push ebp
|
||||
push ecx
|
||||
mov ebp, esp
|
||||
|
||||
xor eax, eax
|
||||
mov eax, [ebp + 8h]
|
||||
mov ecx, [ebp + 0Ch]
|
||||
add eax, ecx
|
||||
|
||||
pop ecx
|
||||
pop ebp
|
||||
retn 4
|
||||
}
|
||||
}
|
||||
|
||||
void AntiCheat::CrashClient()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
#ifdef DEBUG_DETECTIONS
|
||||
Logger::Flush();
|
||||
MessageBoxA(nullptr, "Check the log for more information!", "AntiCheat triggered", MB_ICONERROR);
|
||||
ExitProcess(0xFFFFFFFF);
|
||||
#else
|
||||
static std::thread triggerThread;
|
||||
if (!triggerThread.joinable())
|
||||
{
|
||||
triggerThread = std::thread([]()
|
||||
{
|
||||
std::this_thread::sleep_for(43s);
|
||||
Utils::Hook::Set<BYTE>(0x41BA2C, 0xEB);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::AssertCalleeModule(void* callee)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
HMODULE hModuleSelf = nullptr, hModuleTarget = nullptr, hModuleProcess = GetModuleHandleA(nullptr);
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(callee), &hModuleTarget);
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(AntiCheat::AssertCalleeModule), &hModuleSelf);
|
||||
|
||||
if (!hModuleSelf || !hModuleTarget || !hModuleProcess || (hModuleTarget != hModuleSelf && hModuleTarget != hModuleProcess))
|
||||
{
|
||||
#ifdef DEBUG_DETECTIONS
|
||||
char buffer[MAX_PATH] = { 0 };
|
||||
GetModuleFileNameA(hModuleTarget, buffer, sizeof buffer);
|
||||
|
||||
Logger::Print(Utils::String::VA("AntiCheat: Callee assertion failed: %X %s", reinterpret_cast<uint32_t>(callee), buffer));
|
||||
#endif
|
||||
|
||||
AntiCheat::CrashClient();
|
||||
}
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::InitLoadLibHook()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
static uint8_t kernel32Str[] = { 0xB4, 0x9A, 0x8D, 0xB1, 0x9A, 0x93, 0xCC, 0xCD, 0xD1, 0x9B, 0x93, 0x93 }; // KerNel32.dll
|
||||
static uint8_t loadLibAStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xBE }; // LoadLibraryA
|
||||
static uint8_t loadLibWStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xA8 }; // LoadLibraryW
|
||||
|
||||
HMODULE kernel32 = GetModuleHandleA(Utils::String::XOR(std::string(reinterpret_cast<char*>(kernel32Str), sizeof kernel32Str), -1).data());
|
||||
if (kernel32)
|
||||
{
|
||||
FARPROC loadLibA = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibAStr), sizeof loadLibAStr), -1).data());
|
||||
FARPROC loadLibW = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibWStr), sizeof loadLibWStr), -1).data());
|
||||
|
||||
std::string libExA = Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibAStr), sizeof loadLibAStr), -1);
|
||||
std::string libExW = Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibWStr), sizeof loadLibWStr), -1);
|
||||
|
||||
libExA.insert(libExA.end() - 1, 'E');
|
||||
libExA.insert(libExA.end() - 1, 'x');
|
||||
|
||||
libExW.insert(libExW.end() - 1, 'E');
|
||||
libExW.insert(libExW.end() - 1, 'x');
|
||||
|
||||
FARPROC loadLibExA = GetProcAddress(kernel32, libExA.data());
|
||||
FARPROC loadLibExW = GetProcAddress(kernel32, libExW.data());
|
||||
|
||||
if (loadLibA && loadLibW && loadLibExA && loadLibExW)
|
||||
{
|
||||
#ifdef DEBUG_LOAD_LIBRARY
|
||||
AntiCheat::LoadLibHook[0].initialize(loadLibA, LoadLibaryAStub, HOOK_JUMP);
|
||||
AntiCheat::LoadLibHook[1].initialize(loadLibW, LoadLibaryWStub, HOOK_JUMP);
|
||||
AntiCheat::LoadLibHook[2].initialize(loadLibExA, LoadLibaryExAStub, HOOK_JUMP);
|
||||
AntiCheat::LoadLibHook[3].initialize(loadLibExW, LoadLibaryExWStub, HOOK_JUMP);
|
||||
#else
|
||||
static uint8_t loadLibStub[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; // xor eax, eax; retn 04h
|
||||
static uint8_t loadLibExStub[] = { 0x33, 0xC0, 0xC2, 0x0C, 0x00 }; // xor eax, eax; retn 0Ch
|
||||
AntiCheat::LoadLibHook[0].initialize(loadLibA, loadLibStub, HOOK_JUMP);
|
||||
AntiCheat::LoadLibHook[1].initialize(loadLibW, loadLibStub, HOOK_JUMP);
|
||||
AntiCheat::LoadLibHook[2].initialize(loadLibExA, loadLibExStub, HOOK_JUMP);
|
||||
AntiCheat::LoadLibHook[3].initialize(loadLibExW, loadLibExStub, HOOK_JUMP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t ldrLoadDllStub[] = { 0x33, 0xC0, 0xC2, 0x10, 0x00 };
|
||||
static uint8_t ldrLoadDll[] = { 0xB3, 0x9B, 0x8D, 0xB3, 0x90, 0x9E, 0x9B, 0xBB, 0x93, 0x93 }; // LdrLoadDll
|
||||
|
||||
HMODULE ntdll = Utils::GetNTDLL();
|
||||
//AntiCheat::LoadLibHook[4].initialize(GetProcAddress(ntdll, Utils::String::XOR(std::string(reinterpret_cast<char*>(ldrLoadDll), sizeof ldrLoadDll), -1).data()), ldrLoadDllStub, HOOK_JUMP);
|
||||
|
||||
// Patch LdrpLoadDll
|
||||
Utils::Hook::Signature::Container container;
|
||||
container.signature = "\x8B\xFF\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x33\xC4\x89\x84\x24\x00\x00\x00\x00\x53\x8B\x5D\x10\x56\x57";
|
||||
container.mask = "xxxxxxxxxx????x????xxxxx????xxxxxx";
|
||||
container.callback = [](char* addr)
|
||||
{
|
||||
static uint8_t ldrpLoadDllStub[] = { 0x33, 0xC0, 0xC2, 0x0C, 0x00 };
|
||||
AntiCheat::LoadLibHook[5].initialize(addr, ldrpLoadDllStub, HOOK_JUMP);
|
||||
};
|
||||
|
||||
Utils::Hook::Signature signature(ntdll, Utils::GetModuleSize(ntdll));
|
||||
signature.add(container);
|
||||
//signature.process();
|
||||
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::ReadIntegrityCheck()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
#ifdef PROCTECT_PROCESS
|
||||
static Utils::Time::Interval check;
|
||||
|
||||
if (check.elapsed(20s))
|
||||
{
|
||||
check.update();
|
||||
|
||||
if (HANDLE h = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId()))
|
||||
{
|
||||
#ifdef DEBUG_DETECTIONS
|
||||
Logger::Print("AntiCheat: Process integrity check failed");
|
||||
#endif
|
||||
|
||||
CloseHandle(h);
|
||||
AntiCheat::CrashClient();
|
||||
}
|
||||
}
|
||||
|
||||
// Set the integrity flag
|
||||
AntiCheat::Flags |= AntiCheat::IntergrityFlag::READ_INTEGRITY_CHECK;
|
||||
#endif
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::FlagIntegrityCheck()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
static Utils::Time::Interval check;
|
||||
|
||||
if (check.elapsed(30s))
|
||||
{
|
||||
check.update();
|
||||
|
||||
unsigned long flags = ((AntiCheat::IntergrityFlag::MAX_FLAG - 1) << 1) - 1;
|
||||
|
||||
if (AntiCheat::Flags != flags)
|
||||
{
|
||||
#ifdef DEBUG_DETECTIONS
|
||||
Logger::Print(Utils::String::VA("AntiCheat: Flag integrity check failed: %X", AntiCheat::Flags));
|
||||
#endif
|
||||
|
||||
AntiCheat::CrashClient();
|
||||
}
|
||||
}
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::ScanIntegrityCheck()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
// If there was no check within the last 40 seconds, crash!
|
||||
if (AntiCheat::LastCheck.elapsed(40s))
|
||||
{
|
||||
#ifdef DEBUG_DETECTIONS
|
||||
Logger::Print("AntiCheat: Integrity check failed");
|
||||
#endif
|
||||
|
||||
AntiCheat::CrashClient();
|
||||
}
|
||||
|
||||
// Set the integrity flag
|
||||
AntiCheat::Flags |= AntiCheat::IntergrityFlag::SCAN_INTEGRITY_CHECK;
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::PerformScan()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
static std::optional<unsigned int> hashVal;
|
||||
|
||||
// Perform check only every 20 seconds
|
||||
if (!AntiCheat::LastCheck.elapsed(20s)) return;
|
||||
AntiCheat::LastCheck.update();
|
||||
|
||||
// Hash .text segment
|
||||
// Add 1 to each value, so searching in memory doesn't reveal anything
|
||||
size_t textSize = 0x2D6001;
|
||||
char* textBase = reinterpret_cast<char*>(0x401001);
|
||||
|
||||
unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(textBase - 1, textSize - 1);
|
||||
|
||||
// Set the hash, if none is set
|
||||
if (!hashVal.has_value())
|
||||
{
|
||||
hashVal.emplace(hash);
|
||||
}
|
||||
// Crash if the hashes don't match
|
||||
else if (hashVal.value() != hash)
|
||||
{
|
||||
#ifdef DEBUG_DETECTIONS
|
||||
Logger::Print("AntiCheat: Memory scan failed");
|
||||
#endif
|
||||
|
||||
AntiCheat::CrashClient();
|
||||
}
|
||||
|
||||
// Set the memory scan flag
|
||||
AntiCheat::Flags |= AntiCheat::IntergrityFlag::MEMORY_SCAN;
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::QuickCodeScanner1()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
static Utils::Time::Interval interval;
|
||||
static std::optional<unsigned int> hashVal;
|
||||
|
||||
if (!interval.elapsed(32s)) return;
|
||||
interval.update();
|
||||
|
||||
// Hash .text segment
|
||||
// Add 1 to each value, so searching in memory doesn't reveal anything
|
||||
size_t textSize = 0x2D5FFF;
|
||||
char* textBase = reinterpret_cast<char*>(0x400FFF);
|
||||
unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(textBase + 1, textSize + 1);
|
||||
|
||||
if (hashVal.has_value() && hash != hashVal.value())
|
||||
{
|
||||
Utils::Hook::Set<BYTE>(0x42A667, 0x90); // Crash
|
||||
}
|
||||
|
||||
hashVal.emplace(hash);
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::QuickCodeScanner2()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
static Utils::Time::Interval interval;
|
||||
static std::optional<unsigned int> hashVal;
|
||||
|
||||
if (!interval.elapsed(42s)) return;
|
||||
interval.update();
|
||||
|
||||
// Hash .text segment
|
||||
unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(reinterpret_cast<char*>(0x401000), 0x2D6000);
|
||||
if (hashVal.has_value() && hash != hashVal.value())
|
||||
{
|
||||
Utils::Hook::Set<BYTE>(0x40797C, 0x90); // Crash
|
||||
}
|
||||
|
||||
hashVal.emplace(hash);
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOAD_LIBRARY
|
||||
HANDLE AntiCheat::LoadLibary(std::wstring library, HANDLE file, DWORD flags, void* callee)
|
||||
{
|
||||
HMODULE module;
|
||||
char buffer[MAX_PATH] = { 0 };
|
||||
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(callee), &module);
|
||||
GetModuleFileNameA(module, buffer, sizeof buffer);
|
||||
|
||||
MessageBoxA(nullptr, Utils::String::VA("Loading library %s via %s %X", std::string(library.begin(), library.end()).data(), buffer, reinterpret_cast<uint32_t>(callee)), nullptr, 0);
|
||||
|
||||
AntiCheat::LoadLibHook[3].uninstall();
|
||||
HANDLE h = LoadLibraryExW(library.data(), file, flags);
|
||||
AntiCheat::LoadLibHook[3].install();
|
||||
return h;
|
||||
}
|
||||
|
||||
HANDLE WINAPI AntiCheat::LoadLibaryAStub(const char* library)
|
||||
{
|
||||
std::string lib(library);
|
||||
return AntiCheat::LoadLibary(std::wstring(lib.begin(), lib.end()), nullptr, 0, _ReturnAddress());
|
||||
}
|
||||
|
||||
HANDLE WINAPI AntiCheat::LoadLibaryWStub(const wchar_t* library)
|
||||
{
|
||||
return AntiCheat::LoadLibary(library, nullptr, 0, _ReturnAddress());
|
||||
}
|
||||
|
||||
HANDLE WINAPI AntiCheat::LoadLibaryExAStub(const char* library, HANDLE file, DWORD flags)
|
||||
{
|
||||
std::string lib(library);
|
||||
return AntiCheat::LoadLibary(std::wstring(lib.begin(), lib.end()), file, flags, _ReturnAddress());
|
||||
}
|
||||
|
||||
HANDLE WINAPI AntiCheat::LoadLibaryExWStub(const wchar_t* library, HANDLE file, DWORD flags)
|
||||
{
|
||||
return AntiCheat::LoadLibary(library, file, flags, _ReturnAddress());
|
||||
}
|
||||
#endif
|
||||
|
||||
void AntiCheat::UninstallLibHook()
|
||||
{
|
||||
for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i)
|
||||
{
|
||||
AntiCheat::LoadLibHook[i].uninstall();
|
||||
}
|
||||
}
|
||||
|
||||
void AntiCheat::InstallLibHook()
|
||||
{
|
||||
for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i)
|
||||
{
|
||||
AntiCheat::LoadLibHook[i].install();
|
||||
}
|
||||
}
|
||||
|
||||
void AntiCheat::PatchWinAPI()
|
||||
{
|
||||
LibUnlocker _;
|
||||
|
||||
// Initialize directx
|
||||
Utils::Hook::Call<void()>(0x5078C0)();
|
||||
}
|
||||
|
||||
void AntiCheat::SoundInitStub(int a1, int a2, int a3)
|
||||
{
|
||||
LibUnlocker _;
|
||||
Game::SND_Init(a1, a2, a3);
|
||||
}
|
||||
|
||||
void AntiCheat::SoundInitDriverStub()
|
||||
{
|
||||
LibUnlocker _;
|
||||
Game::SND_InitDriver();
|
||||
}
|
||||
|
||||
void AntiCheat::LostD3DStub()
|
||||
{
|
||||
LibUnlocker _;
|
||||
|
||||
// Reset directx
|
||||
Utils::Hook::Call<void()>(0x508070)();
|
||||
}
|
||||
|
||||
__declspec(naked) void AntiCheat::CinematicStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
call AntiCheat::UninstallLibHook
|
||||
popad
|
||||
|
||||
call Game::R_Cinematic_StartPlayback_Now
|
||||
|
||||
pushad
|
||||
call AntiCheat::InstallLibHook
|
||||
popad
|
||||
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void AntiCheat::DObjGetWorldTagPosStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
push [esp + 20h]
|
||||
|
||||
call AntiCheat::AssertCalleeModule
|
||||
|
||||
pop esi
|
||||
popad
|
||||
|
||||
push ecx
|
||||
mov ecx, [esp + 10h]
|
||||
|
||||
push 426585h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void AntiCheat::AimTargetGetTagPosStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
push [esp + 20h]
|
||||
|
||||
call AntiCheat::AssertCalleeModule
|
||||
|
||||
pop esi
|
||||
popad
|
||||
|
||||
sub esp, 14h
|
||||
cmp dword ptr[esi + 0E0h], 1
|
||||
push 56AC6Ah
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
bool AntiCheat::IsPageChangeAllowed(void* callee, void* addr, size_t len)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
HMODULE hModuleSelf = nullptr, hModuleTarget = nullptr, hModuleMain = GetModuleHandle(nullptr);
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(callee), &hModuleTarget);
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(AntiCheat::IsPageChangeAllowed), &hModuleSelf);
|
||||
|
||||
size_t mainSize = Utils::GetModuleSize(hModuleMain), selfSize = Utils::GetModuleSize(hModuleSelf);
|
||||
DWORD self = DWORD(hModuleSelf), main = DWORD(hModuleMain), address = DWORD(addr);
|
||||
|
||||
// If the address that should be changed is within our module or the main binary, then we need to check if we are changing it or someone else
|
||||
if (Utils::HasIntercection(self, selfSize, address, len) || Utils::HasIntercection(main, mainSize, address, len))
|
||||
{
|
||||
if (!hModuleSelf || !hModuleTarget || (hModuleTarget != hModuleSelf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
__VMProtectEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL WINAPI AntiCheat::VirtualProtectStub(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
if (!AntiCheat::IsPageChangeAllowed(_ReturnAddress(), lpAddress, dwSize)) return FALSE;
|
||||
|
||||
AntiCheat::VirtualProtectHook[0].uninstall(false);
|
||||
BOOL result = VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
|
||||
AntiCheat::VirtualProtectHook[0].install(false);
|
||||
|
||||
__VMProtectEnd;
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL WINAPI AntiCheat::VirtualProtectExStub(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
if (GetCurrentProcessId() == GetProcessId(hProcess) && !AntiCheat::IsPageChangeAllowed(_ReturnAddress(), lpAddress, dwSize)) return FALSE;
|
||||
|
||||
AntiCheat::VirtualProtectHook[1].uninstall(false);
|
||||
BOOL result = VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
|
||||
AntiCheat::VirtualProtectHook[1].install(false);
|
||||
|
||||
__VMProtectEnd;
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned long AntiCheat::ProtectProcess()
|
||||
{
|
||||
#ifdef PROCTECT_PROCESS
|
||||
__VMProtectBeginUltra("");
|
||||
|
||||
Utils::Memory::Allocator allocator;
|
||||
|
||||
HANDLE hToken = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), /*TOKEN_ADJUST_PRIVILEGES | */TOKEN_READ, &hToken))
|
||||
{
|
||||
if (!OpenThreadToken(GetCurrentThread(), /*TOKEN_ADJUST_PRIVILEGES | */TOKEN_READ, TRUE, &hToken))
|
||||
{
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
auto freeSid = [](void* sid)
|
||||
{
|
||||
if (sid)
|
||||
{
|
||||
FreeSid(reinterpret_cast<PSID>(sid));
|
||||
}
|
||||
};
|
||||
|
||||
allocator.reference(hToken, [](void* hToken)
|
||||
{
|
||||
if (hToken)
|
||||
{
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
});
|
||||
|
||||
//AntiCheat::AcquireDebugPrivilege(hToken);
|
||||
|
||||
DWORD dwSize = 0;
|
||||
PVOID pTokenInfo = nullptr;
|
||||
if (GetTokenInformation(hToken, TokenUser, nullptr, 0, &dwSize) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) return GetLastError();
|
||||
|
||||
if (dwSize)
|
||||
{
|
||||
pTokenInfo = allocator.allocate(dwSize);
|
||||
if (!pTokenInfo) return GetLastError();
|
||||
}
|
||||
|
||||
if (!GetTokenInformation(hToken, TokenUser, pTokenInfo, dwSize, &dwSize) || !pTokenInfo) return GetLastError();
|
||||
|
||||
PSID psidCurUser = reinterpret_cast<TOKEN_USER*>(pTokenInfo)->User.Sid;
|
||||
|
||||
PSID psidEveryone = nullptr;
|
||||
SID_IDENTIFIER_AUTHORITY sidEveryone = SECURITY_WORLD_SID_AUTHORITY;
|
||||
if (!AllocateAndInitializeSid(&sidEveryone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone) || !psidEveryone) return GetLastError();
|
||||
allocator.reference(psidEveryone, freeSid);
|
||||
|
||||
PSID psidSystem = nullptr;
|
||||
SID_IDENTIFIER_AUTHORITY sidSystem = SECURITY_NT_AUTHORITY;
|
||||
if (!AllocateAndInitializeSid(&sidSystem, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidSystem) || !psidSystem) return GetLastError();
|
||||
allocator.reference(psidSystem, freeSid);
|
||||
|
||||
PSID psidAdmins = nullptr;
|
||||
SID_IDENTIFIER_AUTHORITY sidAdministrators = SECURITY_NT_AUTHORITY;
|
||||
if (!AllocateAndInitializeSid(&sidAdministrators, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmins) || !psidAdmins) return GetLastError();
|
||||
allocator.reference(psidAdmins, freeSid);
|
||||
|
||||
const PSID psidArray[] =
|
||||
{
|
||||
psidEveryone, /* Deny most rights to everyone */
|
||||
psidCurUser, /* Allow what was not denied */
|
||||
psidSystem, /* Full control */
|
||||
psidAdmins, /* Full control */
|
||||
};
|
||||
|
||||
// Determine required size of the ACL
|
||||
dwSize = sizeof(ACL);
|
||||
|
||||
// First the DENY, then the ALLOW
|
||||
dwSize += GetLengthSid(psidArray[0]);
|
||||
dwSize += sizeof(ACCESS_DENIED_ACE) - sizeof(DWORD);
|
||||
|
||||
for (UINT i = 1; i < _countof(psidArray); ++i)
|
||||
{
|
||||
// DWORD is the SidStart field, which is not used for absolute format
|
||||
dwSize += GetLengthSid(psidArray[i]);
|
||||
dwSize += sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD);
|
||||
}
|
||||
|
||||
PACL pDacl = reinterpret_cast<PACL>(allocator.allocate(dwSize));
|
||||
if (!pDacl || !InitializeAcl(pDacl, dwSize, ACL_REVISION)) return GetLastError();
|
||||
|
||||
// Just give access to what steam needs
|
||||
//static const DWORD dwPoison = 0UL | ~(SYNCHRONIZE | GENERIC_EXECUTE | GENERIC_ALL);
|
||||
static const DWORD dwPoison =
|
||||
/*READ_CONTROL |*/ WRITE_DAC | WRITE_OWNER |
|
||||
PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
|
||||
PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION |
|
||||
PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
|
||||
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
|
||||
// In addition to protected process
|
||||
PROCESS_SUSPEND_RESUME | PROCESS_TERMINATE;
|
||||
|
||||
if (!AddAccessDeniedAce(pDacl, ACL_REVISION, dwPoison, psidArray[0])) return GetLastError();
|
||||
|
||||
// Standard and specific rights not explicitly denied
|
||||
//static const DWORD dwAllowed = 0UL | SYNCHRONIZE;
|
||||
static const DWORD dwAllowed = (~dwPoison & 0x1FFF) | SYNCHRONIZE;
|
||||
if (!AddAccessAllowedAce(pDacl, ACL_REVISION, dwAllowed, psidArray[1])) return GetLastError();
|
||||
|
||||
// Because of ACE ordering, System will effectively have dwAllowed even
|
||||
// though the ACE specifies PROCESS_ALL_ACCESS (unless software uses
|
||||
// SeDebugPrivilege or SeTcbName and increases access).
|
||||
// As an exercise, check behavior of tools such as Process Explorer under XP,
|
||||
// Vista, and above. Vista and above should exhibit slightly different behavior
|
||||
// due to Restricted tokens.
|
||||
if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[2])) return GetLastError();
|
||||
|
||||
// Because of ACE ordering, Administrators will effectively have dwAllowed
|
||||
// even though the ACE specifies PROCESS_ALL_ACCESS (unless the Administrator
|
||||
// invokes 'discretionary security' by taking ownership and increasing access).
|
||||
// As an exercise, check behavior of tools such as Process Explorer under XP,
|
||||
// Vista, and above. Vista and above should exhibit slightly different behavior
|
||||
// due to Restricted tokens.
|
||||
if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[3])) return GetLastError();
|
||||
|
||||
PSECURITY_DESCRIPTOR pSecDesc = allocator.allocate<SECURITY_DESCRIPTOR>();
|
||||
if (!pSecDesc) return GetLastError();
|
||||
|
||||
// InitializeSecurityDescriptor initializes a security descriptor in
|
||||
// absolute format, rather than self-relative format. See
|
||||
// http://msdn.microsoft.com/en-us/library/aa378863(VS.85).aspx
|
||||
if (!InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)) return GetLastError();
|
||||
if (!SetSecurityDescriptorDacl(pSecDesc, TRUE, pDacl, FALSE)) return GetLastError();
|
||||
|
||||
__VMProtectEnd;
|
||||
|
||||
return SetSecurityInfo(
|
||||
GetCurrentProcess(),
|
||||
SE_KERNEL_OBJECT, // process object
|
||||
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
||||
psidCurUser, // NULL, // Owner SID
|
||||
nullptr, // Group SID
|
||||
pDacl,
|
||||
nullptr // SACL
|
||||
);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AntiCheat::AcquireDebugPrivilege(HANDLE hToken)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
|
||||
LUID luid;
|
||||
TOKEN_PRIVILEGES tp = { 0 };
|
||||
DWORD cb = sizeof(TOKEN_PRIVILEGES);
|
||||
if (!LookupPrivilegeValueA(nullptr, SE_DEBUG_NAME, &luid)) return;
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tp, cb, nullptr, nullptr);
|
||||
//if (GetLastError() != ERROR_SUCCESS) return;
|
||||
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::PatchVirtualProtect(void* vp, void* vpex)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
AntiCheat::VirtualProtectHook[1].initialize(vpex, AntiCheat::VirtualProtectExStub, HOOK_JUMP)->install(true, true);
|
||||
AntiCheat::VirtualProtectHook[0].initialize(vp, AntiCheat::VirtualProtectStub, HOOK_JUMP)->install(true, true);
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
NTSTATUS NTAPI AntiCheat::NtCreateThreadExStub(PHANDLE phThread, ACCESS_MASK desiredAccess, LPVOID objectAttributes, HANDLE processHandle, LPTHREAD_START_ROUTINE startAddress, LPVOID parameter, BOOL createSuspended, DWORD stackZeroBits, DWORD sizeOfStackCommit, DWORD sizeOfStackReserve, LPVOID bytesBuffer)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
|
||||
HANDLE hThread = nullptr;
|
||||
std::lock_guard<std::mutex> _(AntiCheat::ThreadMutex);
|
||||
|
||||
AntiCheat::CreateThreadHook.uninstall();
|
||||
NTSTATUS result = NtCreateThreadEx_t(AntiCheat::CreateThreadHook.getAddress())(&hThread, desiredAccess, objectAttributes, processHandle, startAddress, parameter, createSuspended, stackZeroBits, sizeOfStackCommit, sizeOfStackReserve, bytesBuffer);
|
||||
AntiCheat::CreateThreadHook.install();
|
||||
|
||||
if (phThread) *phThread = hThread;
|
||||
|
||||
if (GetProcessId(processHandle) == GetCurrentProcessId())
|
||||
{
|
||||
AntiCheat::OwnThreadIds.push_back(GetThreadId(hThread));
|
||||
}
|
||||
|
||||
__VMProtectEnd;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AntiCheat::PatchThreadCreation()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
|
||||
HMODULE ntdll = Utils::GetNTDLL();
|
||||
if (ntdll)
|
||||
{
|
||||
static uint8_t ntCreateThreadEx[] = { 0xB1, 0x8B, 0xBC, 0x8D, 0x9A, 0x9E, 0x8B, 0x9A, 0xAB, 0x97, 0x8D, 0x9A, 0x9E, 0x9B, 0xBA, 0x87 }; // NtCreateThreadEx
|
||||
FARPROC createThread = GetProcAddress(ntdll, Utils::String::XOR(std::string(reinterpret_cast<char*>(ntCreateThreadEx), sizeof ntCreateThreadEx), -1).data());
|
||||
if (createThread)
|
||||
{
|
||||
AntiCheat::CreateThreadHook.initialize(createThread, AntiCheat::NtCreateThreadExStub, HOOK_JUMP)->install();
|
||||
}
|
||||
}
|
||||
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
int AntiCheat::ValidateThreadTermination(void* addr)
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
{
|
||||
std::lock_guard<std::mutex> _(AntiCheat::ThreadMutex);
|
||||
|
||||
DWORD id = GetCurrentThreadId();
|
||||
auto threadHook = AntiCheat::ThreadHookMap.find(id);
|
||||
if (threadHook != AntiCheat::ThreadHookMap.end())
|
||||
{
|
||||
threadHook->second->uninstall(false);
|
||||
AntiCheat::ThreadHookMap.erase(threadHook); // Uninstall and delete the hook
|
||||
return 1; // Kill
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(AntiCheat::ThreadMutex);
|
||||
|
||||
// It would be better to wait for the thread
|
||||
// but we don't know if there are multiple hooks at the same address
|
||||
bool found = false;
|
||||
for (auto threadHook : AntiCheat::ThreadHookMap)
|
||||
{
|
||||
if (threadHook.second->getAddress() == addr)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) break;
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
__VMProtectEnd;
|
||||
|
||||
return 0; // Don't kill
|
||||
}
|
||||
|
||||
__declspec(naked) void AntiCheat::ThreadEntryPointStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
push eax
|
||||
pushad
|
||||
|
||||
// Reinitialize the return address
|
||||
mov eax, [esp + 28h]
|
||||
sub eax, 5
|
||||
mov [esp + 28h], eax
|
||||
|
||||
push eax
|
||||
call AntiCheat::ValidateThreadTermination
|
||||
add esp, 4h
|
||||
|
||||
mov [esp + 20h], eax
|
||||
|
||||
popad
|
||||
|
||||
pop eax
|
||||
|
||||
test eax, eax
|
||||
jz dontKill
|
||||
|
||||
pop eax
|
||||
add esp, 4h // Remove return address (simulate a jump hook)
|
||||
retn
|
||||
|
||||
dontKill:
|
||||
pop eax
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void AntiCheat::VerifyThreadIntegrity()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
bool kill = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> _(AntiCheat::ThreadMutex);
|
||||
|
||||
auto threadHook = std::find(AntiCheat::OwnThreadIds.begin(), AntiCheat::OwnThreadIds.end(), GetCurrentThreadId());
|
||||
if (threadHook != AntiCheat::OwnThreadIds.end())
|
||||
{
|
||||
AntiCheat::OwnThreadIds.erase(threadHook);
|
||||
kill = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (kill)
|
||||
{
|
||||
static bool first = true;
|
||||
if (first) first = false; // We can't control the main thread, as it's spawned externally
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> _(AntiCheat::ThreadMutex);
|
||||
|
||||
HMODULE ntdll = Utils::GetNTDLL(), targetModule;
|
||||
if (!ntdll) return; // :(
|
||||
|
||||
void* address = Utils::GetThreadStartAddress(GetCurrentThread());
|
||||
if (address)
|
||||
{
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(address), &targetModule);
|
||||
if (targetModule == ntdll) return; // Better not kill kernel threads
|
||||
|
||||
DWORD id = GetCurrentThreadId();
|
||||
{
|
||||
auto threadHook = AntiCheat::ThreadHookMap.find(id);
|
||||
if (threadHook != AntiCheat::ThreadHookMap.end())
|
||||
{
|
||||
threadHook->second->uninstall(false);
|
||||
AntiCheat::ThreadHookMap.erase(threadHook);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Utils::Hook> hook = std::make_shared<Utils::Hook>();
|
||||
AntiCheat::ThreadHookMap[id] = hook;
|
||||
|
||||
// Hook the entry point of the thread to properly terminate it
|
||||
hook->initialize(address, AntiCheat::ThreadEntryPointStub, HOOK_CALL)->install(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::SystemTimeDiff(LPSYSTEMTIME stA, LPSYSTEMTIME stB, LPSYSTEMTIME stC) {
|
||||
FILETIME ftA, ftB, ftC;
|
||||
ULARGE_INTEGER uiA, uiB, uiC;
|
||||
|
||||
SystemTimeToFileTime(stA, &ftA);
|
||||
SystemTimeToFileTime(stB, &ftB);
|
||||
uiA.HighPart = ftA.dwHighDateTime;
|
||||
uiA.LowPart = ftA.dwLowDateTime;
|
||||
uiB.HighPart = ftB.dwHighDateTime;
|
||||
uiB.LowPart = ftB.dwLowDateTime;
|
||||
|
||||
uiC.QuadPart = uiA.QuadPart - uiB.QuadPart;
|
||||
|
||||
ftC.dwHighDateTime = uiC.HighPart;
|
||||
ftC.dwLowDateTime = uiC.LowPart;
|
||||
FileTimeToSystemTime(&ftC, stC);
|
||||
}
|
||||
|
||||
void AntiCheat::CheckStartupTime()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
FILETIME creation, exit, kernel, user;
|
||||
SYSTEMTIME current, creationSt, diffSt;
|
||||
|
||||
GetSystemTime(¤t);
|
||||
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
|
||||
|
||||
FileTimeToSystemTime(&creation, &creationSt);
|
||||
AntiCheat::SystemTimeDiff(¤t, &creationSt, &diffSt);
|
||||
|
||||
#ifdef DEBUG
|
||||
char buf[512];
|
||||
snprintf(buf, 512, "creation: %d:%d:%d:%d\n", creationSt.wHour, creationSt.wMinute, creationSt.wSecond, creationSt.wMilliseconds);
|
||||
OutputDebugStringA(buf);
|
||||
|
||||
snprintf(buf, 512, "current: %d:%d:%d:%d\n", current.wHour, current.wMinute, current.wSecond, current.wMilliseconds);
|
||||
OutputDebugStringA(buf);
|
||||
|
||||
snprintf(buf, 512, "diff: %d:%d:%d:%d\n", diffSt.wHour, diffSt.wMinute, diffSt.wSecond, diffSt.wMilliseconds);
|
||||
OutputDebugStringA(buf);
|
||||
#endif
|
||||
|
||||
// crash client if they are using process suspension to inject dlls during startup (aka before we got to here)
|
||||
// maybe tweak this value depending on what the above logging reveals during testing,
|
||||
// but 5 seconds seems about right for now
|
||||
int time = diffSt.wMilliseconds + (diffSt.wSecond * 1000) + (diffSt.wMinute * 1000 * 60);
|
||||
if (time > 5000) {
|
||||
Components::AntiCheat::CrashClient();
|
||||
}
|
||||
|
||||
// use below for logging when using StartSuspended.exe
|
||||
// FILE* f = fopen("times.txt", "a");
|
||||
// fwrite(buf, 1, strlen(buf), f);
|
||||
// fclose(f);
|
||||
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
AntiCheat::AntiCheat()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
|
||||
time(nullptr);
|
||||
AntiCheat::Flags = NO_FLAG;
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
|
||||
Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x418209, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x60BE9D, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x60BE8E, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x418204, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick();
|
||||
Scheduler::OnFrame(AntiCheat::PerformScan, true);
|
||||
|
||||
// Detect aimbots
|
||||
Utils::Hook(0x426580, AntiCheat::DObjGetWorldTagPosStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x56AC60, AntiCheat::AimTargetGetTagPosStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// TODO: Probably move that :P
|
||||
if (!Dedicated::IsEnabled())
|
||||
{
|
||||
AntiCheat::InitLoadLibHook();
|
||||
}
|
||||
|
||||
// Prevent external processes from accessing our memory
|
||||
AntiCheat::ProtectProcess();
|
||||
Renderer::OnDeviceRecoveryEnd([]()
|
||||
{
|
||||
AntiCheat::ProtectProcess();
|
||||
});
|
||||
|
||||
// Set the integrity flag
|
||||
AntiCheat::Flags |= AntiCheat::IntergrityFlag::INITIALIZATION;
|
||||
|
||||
#endif
|
||||
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
AntiCheat::~AntiCheat()
|
||||
{
|
||||
AntiCheat::Flags = NO_FLAG;
|
||||
AntiCheat::OwnThreadIds.clear();
|
||||
AntiCheat::ThreadHookMap.clear();
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i)
|
||||
{
|
||||
AntiCheat::LoadLibHook[i].uninstall();
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(AntiCheat::VirtualProtectHook); ++i)
|
||||
{
|
||||
AntiCheat::VirtualProtectHook[i].uninstall(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef DEBUG
|
||||
// Hide AntiCheat in embeded symbol names
|
||||
#define AntiCheat SubComponent
|
||||
#else
|
||||
# ifndef DISABLE_ANTICHEAT
|
||||
# define DISABLE_ANTICHEAT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Uncomment to enable process protection (conflicts with steam!)
|
||||
#define PROCTECT_PROCESS
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class AntiCheat : public Component
|
||||
{
|
||||
public:
|
||||
AntiCheat();
|
||||
~AntiCheat();
|
||||
|
||||
class LibUnlocker
|
||||
{
|
||||
public:
|
||||
LibUnlocker()
|
||||
{
|
||||
UninstallLibHook();
|
||||
}
|
||||
~LibUnlocker()
|
||||
{
|
||||
InstallLibHook();
|
||||
}
|
||||
};
|
||||
|
||||
static void CrashClient();
|
||||
|
||||
static void InitLoadLibHook();
|
||||
|
||||
static void ReadIntegrityCheck();
|
||||
static void ScanIntegrityCheck();
|
||||
static void FlagIntegrityCheck();
|
||||
|
||||
static unsigned long ProtectProcess();
|
||||
|
||||
static void PatchVirtualProtect(void* vp, void* vpex);
|
||||
static void PatchThreadCreation();
|
||||
|
||||
static void VerifyThreadIntegrity();
|
||||
|
||||
static void QuickCodeScanner1();
|
||||
static void QuickCodeScanner2();
|
||||
|
||||
static void UninstallLibHook();
|
||||
static void InstallLibHook();
|
||||
|
||||
static void CheckStartupTime();
|
||||
static void SystemTimeDiff(LPSYSTEMTIME stA, LPSYSTEMTIME stB, LPSYSTEMTIME stC);
|
||||
|
||||
private:
|
||||
enum IntergrityFlag
|
||||
{
|
||||
NO_FLAG = (0),
|
||||
INITIALIZATION = (1 << 0),
|
||||
MEMORY_SCAN = (1 << 1),
|
||||
SCAN_INTEGRITY_CHECK = (1 << 2),
|
||||
|
||||
#ifdef PROCTECT_PROCESS
|
||||
READ_INTEGRITY_CHECK = (1 << 3),
|
||||
#endif
|
||||
|
||||
MAX_FLAG,
|
||||
};
|
||||
|
||||
static Utils::Time::Interval LastCheck;
|
||||
static unsigned long Flags;
|
||||
|
||||
static void PerformScan();
|
||||
static void PatchWinAPI();
|
||||
|
||||
static void NullSub();
|
||||
|
||||
static bool IsPageChangeAllowed(void* callee, void* addr, size_t len);
|
||||
static void AssertCalleeModule(void* callee);
|
||||
|
||||
#ifdef DEBUG_LOAD_LIBRARY
|
||||
static HANDLE LoadLibary(std::wstring library, HANDLE file, DWORD flags, void* callee);
|
||||
static HANDLE WINAPI LoadLibaryAStub(const char* library);
|
||||
static HANDLE WINAPI LoadLibaryWStub(const wchar_t* library);
|
||||
static HANDLE WINAPI LoadLibaryExAStub(const char* library, HANDLE file, DWORD flags);
|
||||
static HANDLE WINAPI LoadLibaryExWStub(const wchar_t* library, HANDLE file, DWORD flags);
|
||||
#endif
|
||||
|
||||
static BOOL WINAPI VirtualProtectStub(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
static BOOL WINAPI VirtualProtectExStub(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
|
||||
static void LostD3DStub();
|
||||
static void CinematicStub();
|
||||
static void SoundInitStub(int a1, int a2, int a3);
|
||||
static void SoundInitDriverStub();
|
||||
|
||||
static void DObjGetWorldTagPosStub();
|
||||
static void AimTargetGetTagPosStub();
|
||||
|
||||
static void AcquireDebugPrivilege(HANDLE hToken);
|
||||
|
||||
static NTSTATUS NTAPI NtCreateThreadExStub(PHANDLE hThread, ACCESS_MASK desiredAccess, LPVOID objectAttributes, HANDLE processHandle, LPTHREAD_START_ROUTINE startAddress, LPVOID parameter, BOOL createSuspended, DWORD stackZeroBits, DWORD sizeOfStackCommit, DWORD sizeOfStackReserve, LPVOID bytesBuffer);
|
||||
static int ValidateThreadTermination(void* addr);
|
||||
static void ThreadEntryPointStub();
|
||||
|
||||
static std::mutex ThreadMutex;
|
||||
static std::vector<DWORD> OwnThreadIds;
|
||||
static std::map<DWORD, std::shared_ptr<Utils::Hook>> ThreadHookMap;
|
||||
|
||||
static Utils::Hook CreateThreadHook;
|
||||
static Utils::Hook LoadLibHook[6];
|
||||
static Utils::Hook VirtualProtectHook[2];
|
||||
};
|
||||
}
|
||||
|
@ -520,7 +520,7 @@ namespace Components
|
||||
{
|
||||
for (auto& asset : AssetHandler::EmptyAssets)
|
||||
{
|
||||
Game::Sys_Error(25, reinterpret_cast<char*>(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data());
|
||||
Game::Com_PrintWarning(25, reinterpret_cast<const char*>(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data());
|
||||
}
|
||||
|
||||
AssetHandler::EmptyAssets.clear();
|
||||
|
@ -174,7 +174,7 @@ namespace Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (*Game::svs_numclients <= num)
|
||||
if (*Game::svs_clientCount <= num)
|
||||
{
|
||||
Logger::Print("Player %d is not on the server\n", num);
|
||||
return;
|
||||
@ -185,9 +185,9 @@ namespace Components
|
||||
SteamID guid;
|
||||
guid.bits = client->steamID;
|
||||
|
||||
Bans::InsertBan({ guid, client->netchan.remoteAddress.ip });
|
||||
Bans::InsertBan({guid, client->netchan.remoteAddress.ip});
|
||||
|
||||
Game::SV_KickClientError(client, reason);
|
||||
Game::SV_GameDropClient(num, reason.data());
|
||||
}
|
||||
|
||||
void Bans::UnbanClient(SteamID id)
|
||||
|
@ -1,104 +1,61 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#define KEY_MASK_FIRE 1
|
||||
#define KEY_MASK_SPRINT 2
|
||||
#define KEY_MASK_MELEE 4
|
||||
#define KEY_MASK_RELOAD 16
|
||||
#define KEY_MASK_LEANLEFT 64
|
||||
#define KEY_MASK_LEANRIGHT 128
|
||||
#define KEY_MASK_PRONE 256
|
||||
#define KEY_MASK_CROUCH 512
|
||||
#define KEY_MASK_JUMP 1024
|
||||
#define KEY_MASK_ADS_MODE 2048
|
||||
#define KEY_MASK_TEMP_ACTION 4096
|
||||
#define KEY_MASK_HOLDBREATH 8192
|
||||
#define KEY_MASK_FRAG 16384
|
||||
#define KEY_MASK_SMOKE 32768
|
||||
#define KEY_MASK_NIGHTVISION 262144
|
||||
#define KEY_MASK_ADS 524288
|
||||
#define KEY_MASK_USE 0x28
|
||||
|
||||
#define MAX_G_BOTAI_ENTRIES 18
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::vector<std::string> Bots::BotNames;
|
||||
|
||||
typedef struct BotMovementInfo_t
|
||||
struct BotMovementInfo
|
||||
{
|
||||
/* Actions */
|
||||
int buttons;
|
||||
/* Movement */
|
||||
int8 forward;
|
||||
int8 right;
|
||||
/* Weapon */
|
||||
unsigned short weapon;
|
||||
} BotMovementInfo_t;
|
||||
int buttons; // Actions
|
||||
int8_t forward;
|
||||
int8_t right;
|
||||
uint16_t weapon;
|
||||
bool active;
|
||||
};
|
||||
|
||||
static BotMovementInfo_t g_botai[MAX_G_BOTAI_ENTRIES];
|
||||
static BotMovementInfo g_botai[18];
|
||||
|
||||
struct BotAction_t
|
||||
struct BotAction
|
||||
{
|
||||
const char* action;
|
||||
std::string action;
|
||||
int key;
|
||||
};
|
||||
|
||||
static const BotAction_t BotActions[] =
|
||||
static const BotAction BotActions[] =
|
||||
{
|
||||
{ "gostand", KEY_MASK_JUMP },
|
||||
{ "gocrouch", KEY_MASK_CROUCH },
|
||||
{ "goprone", KEY_MASK_PRONE },
|
||||
{ "fire", KEY_MASK_FIRE },
|
||||
{ "melee", KEY_MASK_MELEE },
|
||||
{ "frag", KEY_MASK_FRAG },
|
||||
{ "smoke", KEY_MASK_SMOKE },
|
||||
{ "reload", KEY_MASK_RELOAD },
|
||||
{ "sprint", KEY_MASK_SPRINT },
|
||||
{ "leanleft", KEY_MASK_LEANLEFT },
|
||||
{ "leanright", KEY_MASK_LEANRIGHT },
|
||||
{ "ads", KEY_MASK_ADS_MODE },
|
||||
{ "holdbreath", KEY_MASK_HOLDBREATH },
|
||||
{ "use", KEY_MASK_USE },
|
||||
{ "0", 8 },
|
||||
{ "1", 32 },
|
||||
{ "2", 65536 },
|
||||
{ "3", 131072 },
|
||||
{ "4", 1048576 },
|
||||
{ "5", 2097152 },
|
||||
{ "6", 4194304 },
|
||||
{ "7", 8388608 },
|
||||
{ "8", 16777216 },
|
||||
{ "9", 33554432 },
|
||||
{ "gostand", Game::usercmdButtonBits::CMD_BUTTON_UP },
|
||||
{ "gocrouch", Game::usercmdButtonBits::CMD_BUTTON_CROUCH },
|
||||
{ "goprone", Game::usercmdButtonBits::CMD_BUTTON_PRONE },
|
||||
{ "fire", Game::usercmdButtonBits::CMD_BUTTON_ATTACK },
|
||||
{ "melee", Game::usercmdButtonBits::CMD_BUTTON_MELEE },
|
||||
{ "frag", Game::usercmdButtonBits::CMD_BUTTON_FRAG },
|
||||
{ "smoke", Game::usercmdButtonBits::CMD_BUTTON_OFFHAND_SECONDARY },
|
||||
{ "reload", Game::usercmdButtonBits::CMD_BUTTON_RELOAD },
|
||||
{ "sprint", Game::usercmdButtonBits::CMD_BUTTON_SPRINT },
|
||||
{ "leanleft", Game::usercmdButtonBits::CMD_BUTTON_LEAN_LEFT },
|
||||
{ "leanright", Game::usercmdButtonBits::CMD_BUTTON_LEAN_RIGHT },
|
||||
{ "ads", Game::usercmdButtonBits::CMD_BUTTON_ADS },
|
||||
{ "holdbreath", Game::usercmdButtonBits::CMD_BUTTON_BREATH },
|
||||
{ "usereload", Game::usercmdButtonBits::CMD_BUTTON_USE_RELOAD },
|
||||
{ "activate", Game::usercmdButtonBits::CMD_BUTTON_ACTIVATE },
|
||||
};
|
||||
|
||||
unsigned int Bots::GetClientNum(Game::client_s* cl)
|
||||
int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
|
||||
{
|
||||
unsigned int num;
|
||||
|
||||
num = ((byte*)cl - (byte*)Game::svs_clients) / sizeof(Game::client_s);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
bool Bots::IsValidClientNum(unsigned int cNum)
|
||||
{
|
||||
return (cNum >= 0) && (cNum < (unsigned int)*Game::svs_numclients);
|
||||
}
|
||||
|
||||
void Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
|
||||
{
|
||||
static int botId = 0;
|
||||
static size_t botId = 0;
|
||||
static bool loadedNames = false; // Load file only once
|
||||
const char* botName;
|
||||
|
||||
if (Bots::BotNames.empty())
|
||||
if (Bots::BotNames.empty() && !loadedNames)
|
||||
{
|
||||
FileSystem::File bots("bots.txt");
|
||||
loadedNames = true;
|
||||
|
||||
if (bots.exists())
|
||||
{
|
||||
std::vector<std::string> names = Utils::String::Split(bots.getBuffer(), '\n');
|
||||
auto names = Utils::String::Split(bots.getBuffer(), '\n');
|
||||
|
||||
for (auto name : names)
|
||||
for (auto& name : names)
|
||||
{
|
||||
Utils::String::Replace(name, "\r", "");
|
||||
name = Utils::String::Trim(name);
|
||||
@ -121,276 +78,253 @@ namespace Components
|
||||
botName = Utils::String::VA("bot%d", ++botId);
|
||||
}
|
||||
|
||||
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, botName, protocol, checksum, statVer, statStuff, port), 0x400);
|
||||
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, protocol, checksum, statVer, statStuff, port);
|
||||
}
|
||||
|
||||
void Bots::Spawn(unsigned int count)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
for (auto i = 0u; i < count; ++i)
|
||||
{
|
||||
Scheduler::OnDelay([]()
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
auto* ent = Game::SV_AddTestClient();
|
||||
if (ent == nullptr)
|
||||
return;
|
||||
|
||||
Scheduler::OnDelay([ent]()
|
||||
{
|
||||
Game::gentity_t* entRef = Game::SV_AddTestClient();
|
||||
if (entRef)
|
||||
Game::Scr_AddString("autoassign");
|
||||
Game::Scr_AddString("team_marinesopfor");
|
||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
||||
|
||||
Scheduler::OnDelay([ent]()
|
||||
{
|
||||
Scheduler::OnDelay([entRef]()
|
||||
{
|
||||
Game::Scr_AddString("autoassign");
|
||||
Game::Scr_AddString("team_marinesopfor");
|
||||
Game::Scr_Notify(entRef, Game::SL_GetString("menuresponse", 0), 2);
|
||||
|
||||
Scheduler::OnDelay([entRef]()
|
||||
{
|
||||
Game::Scr_AddString(Utils::String::VA("class%d", Utils::Cryptography::Rand::GenerateInt() % 5));
|
||||
Game::Scr_AddString("changeclass");
|
||||
Game::Scr_Notify(entRef, Game::SL_GetString("menuresponse", 0), 2);
|
||||
}, 1s);
|
||||
}, 1s);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
|
||||
Game::Scr_AddString("changeclass");
|
||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
||||
}, 1s);
|
||||
}, 1s);
|
||||
}, 500ms * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void Bots::AddMethods()
|
||||
{
|
||||
Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing(<int>)
|
||||
Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_INTEGER)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Needs one integer parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto ping = Game::Scr_GetInt(0);
|
||||
|
||||
if (ping < 0 || ping > 999)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Ping needs to between 0 and 999!\n");
|
||||
return;
|
||||
}
|
||||
ping = std::clamp(ping, 0, 999);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
auto* client = Script::GetClient(ent);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Need to call on a connected player!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
client->ping = (short)ping;
|
||||
client->ping = static_cast<int16_t>(ping);
|
||||
});
|
||||
|
||||
Script::AddFunction("isBot", [](Game::scr_entref_t id) // Usage: <bot> isBot();
|
||||
Script::AddMethod("IsTestClient", [](Game::scr_entref_t entref) // Usage: <bot> IsTestClient();
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* gentity = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(gentity);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1isBot: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1isBot: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(client->isBot);
|
||||
Game::Scr_AddBool(client->bIsTestClient == 1);
|
||||
});
|
||||
|
||||
Script::AddFunction("botStop", [](Game::scr_entref_t id) // Usage: <bot> botStop();
|
||||
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Need to call on a player entity!\n");
|
||||
Game::Scr_Error("^1BotStop: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
g_botai[clientNum] = { 0 };
|
||||
g_botai[clientNum].weapon = 1;
|
||||
g_botai[entref.entnum] = {0};
|
||||
g_botai[entref.entnum].weapon = 1;
|
||||
g_botai[entref.entnum].active = true;
|
||||
});
|
||||
|
||||
Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: <bot> botWeapon(<str>);
|
||||
Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: <bot> BotWeapon(<str>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
const auto* weapon = Game::Scr_GetString(0);
|
||||
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Needs one string parameter!\n");
|
||||
Game::Scr_Error("^1BotWeapon: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto weapon = Game::Scr_GetString(0);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
if (weapon == nullptr || weapon[0] == '\0')
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Need to call on a player entity!\n");
|
||||
g_botai[entref.entnum].weapon = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (weapon == ""s)
|
||||
{
|
||||
g_botai[clientNum].weapon = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
int weapId = Game::G_GetWeaponIndexForName(weapon);
|
||||
|
||||
g_botai[clientNum].weapon = (unsigned short)weapId;
|
||||
const auto weapId = Game::G_GetWeaponIndexForName(weapon);
|
||||
g_botai[entref.entnum].weapon = static_cast<uint16_t>(weapId);
|
||||
g_botai[entref.entnum].active = true;
|
||||
});
|
||||
|
||||
Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: <bot> botAction(<str action>);
|
||||
Script::AddMethod("BotAction", [](Game::scr_entref_t entref) // Usage: <bot> BotAction(<str action>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
const auto* action = Game::Scr_GetString(0);
|
||||
|
||||
if (action == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Needs one string parameter!\n");
|
||||
Game::Scr_ParamError(0, "^1BotAction: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = Game::Scr_GetString(0);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Need to call on a player entity!\n");
|
||||
Game::Scr_Error("^1BotAction: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
if (action[0] != '+' && action[0] != '-')
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Sign for action must be '+' or '-'.\n");
|
||||
Game::Scr_ParamError(0, "^1BotAction: Sign for action must be '+' or '-'.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(BotActions) / sizeof(BotAction_t); ++i)
|
||||
for (auto i = 0u; i < std::extent_v<decltype(BotActions)>; ++i)
|
||||
{
|
||||
if (strcmp(&action[1], BotActions[i].action))
|
||||
if (Utils::String::ToLower(&action[1]) != BotActions[i].action)
|
||||
continue;
|
||||
|
||||
if (action[0] == '+')
|
||||
g_botai[clientNum].buttons |= BotActions[i].key;
|
||||
g_botai[entref.entnum].buttons |= BotActions[i].key;
|
||||
else
|
||||
g_botai[clientNum].buttons &= ~(BotActions[i].key);
|
||||
g_botai[entref.entnum].buttons &= ~BotActions[i].key;
|
||||
|
||||
g_botai[entref.entnum].active = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_Error("^1botAction: Unknown action.\n");
|
||||
Game::Scr_ParamError(0, "^1BotAction: Unknown action.\n");
|
||||
});
|
||||
|
||||
Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: <bot> botMovement(<int>, <int>);
|
||||
Script::AddMethod("BotMovement", [](Game::scr_entref_t entref) // Usage: <bot> BotMovement(<int>, <int>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Needs two integer parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto forwardInt = Game::Scr_GetInt(0);
|
||||
auto rightInt = Game::Scr_GetInt(1);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Need to call on a player entity!\n");
|
||||
Game::Scr_Error("^1BotMovement: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
forwardInt = std::clamp<int>(forwardInt, std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
|
||||
rightInt = std::clamp<int>(rightInt, std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (forwardInt > 127)
|
||||
forwardInt = 127;
|
||||
if (forwardInt < -127)
|
||||
forwardInt = -127;
|
||||
if (rightInt > 127)
|
||||
rightInt = 127;
|
||||
if (rightInt < -127)
|
||||
rightInt = -127;
|
||||
|
||||
g_botai[clientNum].forward = (int8)forwardInt;
|
||||
g_botai[clientNum].right = (int8)rightInt;
|
||||
g_botai[entref.entnum].forward = static_cast<int8_t>(forwardInt);
|
||||
g_botai[entref.entnum].right = static_cast<int8_t>(rightInt);
|
||||
g_botai[entref.entnum].active = true;
|
||||
});
|
||||
}
|
||||
|
||||
void Bots::BotAiAction(Game::client_t* cl)
|
||||
{
|
||||
if (cl->gentity == nullptr)
|
||||
return;
|
||||
|
||||
const auto entnum = cl->gentity->s.number;
|
||||
|
||||
// Keep test client functionality
|
||||
if (!g_botai[entnum].active)
|
||||
{
|
||||
Game::SV_BotUserMove(cl);
|
||||
return;
|
||||
}
|
||||
|
||||
Game::usercmd_s userCmd = {0};
|
||||
|
||||
userCmd.serverTime = *Game::svs_time;
|
||||
|
||||
userCmd.buttons = g_botai[entnum].buttons;
|
||||
userCmd.forwardmove = g_botai[entnum].forward;
|
||||
userCmd.rightmove = g_botai[entnum].right;
|
||||
userCmd.weapon = g_botai[entnum].weapon;
|
||||
|
||||
Game::SV_ClientThink(cl, &userCmd);
|
||||
}
|
||||
|
||||
constexpr auto SV_BotUserMove = 0x626E50;
|
||||
__declspec(naked) void Bots::SV_BotUserMove_Hk()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push edi
|
||||
call Bots::BotAiAction
|
||||
add esp, 4
|
||||
|
||||
popad
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
void Bots::G_SelectWeaponIndex(int clientNum, int iWeaponIndex)
|
||||
{
|
||||
if (g_botai[clientNum].active)
|
||||
{
|
||||
g_botai[clientNum].weapon = static_cast<uint16_t>(iWeaponIndex);
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Bots::G_SelectWeaponIndex_Hk()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push [esp + 0x20 + 0x8]
|
||||
push [esp + 0x20 + 0x8]
|
||||
call Bots::G_SelectWeaponIndex
|
||||
add esp, 0x8
|
||||
|
||||
popad
|
||||
|
||||
// Code skipped by hook
|
||||
mov eax, [esp + 0x8]
|
||||
push eax
|
||||
|
||||
push 0x441B85
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Should be called when a client drops from the server
|
||||
* but not "between levels" (Quake-III-Arena)
|
||||
*/
|
||||
void Bots::ClientDisconnect_Hk(int clientNum)
|
||||
{
|
||||
g_botai[clientNum].active = false;
|
||||
|
||||
// Call original function
|
||||
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum);
|
||||
}
|
||||
|
||||
Bots::Bots()
|
||||
{
|
||||
// Replace connect string
|
||||
@ -399,61 +333,48 @@ namespace Components
|
||||
// Intercept sprintf for the connect string
|
||||
Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick();
|
||||
|
||||
// Stop default behavour of bots spinning and shooting
|
||||
Utils::Hook(0x627021, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627241, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627021, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627241, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
// zero the bot command array
|
||||
for (int i = 0; i < MAX_G_BOTAI_ENTRIES; i++)
|
||||
Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Reset BotMovementInfo.active when client is dropped
|
||||
Utils::Hook(0x625235, Bots::ClientDisconnect_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
// Zero the bot command array
|
||||
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
|
||||
{
|
||||
g_botai[i] = { 0 };
|
||||
g_botai[i].weapon = 1; // prevent the bots from defaulting to the 'none' weapon
|
||||
g_botai[i] = {0};
|
||||
g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon
|
||||
}
|
||||
|
||||
// have the bots perform the command every server frame
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
if (!Game::SV_Loaded())
|
||||
return;
|
||||
|
||||
int time = *Game::svs_time;
|
||||
int numClients = *Game::svs_numclients;
|
||||
for (int i = 0; i < numClients; ++i)
|
||||
{
|
||||
Game::client_t* client = &Game::svs_clients[i];
|
||||
|
||||
if (client->state < 3)
|
||||
continue;
|
||||
|
||||
if (!client->isBot)
|
||||
continue;
|
||||
|
||||
Game::usercmd_s ucmd = { 0 };
|
||||
|
||||
ucmd.serverTime = time;
|
||||
|
||||
ucmd.buttons = g_botai[i].buttons;
|
||||
ucmd.forwardmove = g_botai[i].forward;
|
||||
ucmd.rightmove = g_botai[i].right;
|
||||
ucmd.weapon = g_botai[i].weapon;
|
||||
|
||||
client->deltaMessage = client->netchan.outgoingSequence - 1;
|
||||
|
||||
Game::SV_ClientThink(client, &ucmd);
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("spawnBot", [](Command::Params* params)
|
||||
{
|
||||
unsigned int count = 1;
|
||||
auto count = 1u;
|
||||
|
||||
if (params->size() > 1)
|
||||
{
|
||||
if (params->get(1) == "all"s) count = static_cast<unsigned int>(-1);
|
||||
else count = atoi(params->get(1));
|
||||
if (params->get(1) == "all"s)
|
||||
{
|
||||
count = *Game::svs_clientCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* end;
|
||||
const auto* input = params->get(1);
|
||||
count = std::strtoul(input, &end, 10);
|
||||
|
||||
if (input == end)
|
||||
{
|
||||
Logger::Print("Warning: %s is not a valid input\n"
|
||||
"Usage: %s optional <number of bots> or optional <\"all\">\n",
|
||||
input, params->get(0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = std::min(18u, count);
|
||||
count = std::min(static_cast<unsigned int>(*Game::svs_clientCount), count);
|
||||
|
||||
// Check if ingame and host
|
||||
if (!Game::SV_Loaded())
|
||||
@ -470,10 +391,14 @@ namespace Components
|
||||
});
|
||||
|
||||
Bots::AddMethods();
|
||||
}
|
||||
|
||||
Bots::~Bots()
|
||||
{
|
||||
Bots::BotNames.clear();
|
||||
// In case a loaded mod didn't call "BotStop" before the VM shutdown
|
||||
Script::OnVMShutdown([]
|
||||
{
|
||||
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
|
||||
{
|
||||
g_botai[i].active = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,22 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
Bots();
|
||||
~Bots();
|
||||
static unsigned int GetClientNum(Game::client_s*);
|
||||
static bool IsValidClientNum(unsigned int);
|
||||
|
||||
private:
|
||||
static std::vector<std::string> BotNames;
|
||||
|
||||
static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
||||
static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
||||
|
||||
static void Spawn(unsigned int count);
|
||||
|
||||
static void AddMethods();
|
||||
|
||||
static void BotAiAction(Game::client_t* cl);
|
||||
static void SV_BotUserMove_Hk();
|
||||
|
||||
static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex);
|
||||
static void G_SelectWeaponIndex_Hk();
|
||||
|
||||
static void ClientDisconnect_Hk(int clientNum);
|
||||
};
|
||||
}
|
||||
|
60
src/Components/Modules/Bullet.cpp
Normal file
60
src/Components/Modules/Bullet.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Dvar::Var Bullet::BGSurfacePenetration;
|
||||
Game::dvar_t* Bullet::BGBulletRange;
|
||||
|
||||
float Bullet::BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType)
|
||||
{
|
||||
assert(weapDef != nullptr);
|
||||
assert(weapDef->penetrateType != Game::PenetrateType::PENETRATE_TYPE_NONE);
|
||||
assert(weapDef->penetrateType < Game::PenetrateType::PENETRATE_TYPE_COUNT);
|
||||
assert(static_cast<size_t>(surfaceType) < Game::materialSurfType_t::SURF_TYPE_COUNT);
|
||||
|
||||
const auto penetrationDepth = BGSurfacePenetration.get<float>();
|
||||
if (penetrationDepth > 0.0f)
|
||||
{
|
||||
// Custom depth
|
||||
return penetrationDepth;
|
||||
}
|
||||
|
||||
// Game's code
|
||||
if (surfaceType != Game::materialSurfType_t::SURF_TYPE_DEFAULT)
|
||||
{
|
||||
return (*Game::penetrationDepthTable)[weapDef->penetrateType][surfaceType];
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
__declspec(naked) void Bullet::Bullet_FireStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
mov eax, BGBulletRange
|
||||
fld dword ptr [eax + 0x10] // dvar_t.current.value
|
||||
pop eax
|
||||
|
||||
push 0x440346
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
Bullet::Bullet()
|
||||
{
|
||||
Dvar::OnInit([]
|
||||
{
|
||||
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
||||
0.0f, std::numeric_limits<float>::max(), Game::dvar_flag::DVAR_CODINFO,
|
||||
"Set to a value greater than 0 to override the surface penetration depth");
|
||||
BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f,
|
||||
0.0f, std::numeric_limits<float>::max(), Game::dvar_flag::DVAR_CODINFO,
|
||||
"Max range used when calculating the bullet end position");
|
||||
});
|
||||
|
||||
Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
}
|
19
src/Components/Modules/Bullet.hpp
Normal file
19
src/Components/Modules/Bullet.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Bullet : public Component
|
||||
{
|
||||
public:
|
||||
Bullet();
|
||||
|
||||
private:
|
||||
static Dvar::Var BGSurfacePenetration;
|
||||
// Can't use Var class inside assembly stubs
|
||||
static Game::dvar_t* BGBulletRange;
|
||||
|
||||
static float BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType);
|
||||
|
||||
static void Bullet_FireStub();
|
||||
};
|
||||
}
|
@ -218,8 +218,5 @@ namespace Components
|
||||
|
||||
// This is placed here in case the anticheat has been disabled!
|
||||
// This checks specifically for launching the process suspended to inject a dll
|
||||
#if !defined(DISABLE_ANTICHEAT)
|
||||
AntiCheat::CheckStartupTime();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ namespace Components
|
||||
std::uint8_t padding3[4];
|
||||
std::int32_t tableColumn;
|
||||
};
|
||||
|
||||
struct playercarddata_s
|
||||
{
|
||||
std::uint32_t padding;
|
||||
|
@ -55,9 +55,5 @@ namespace Components
|
||||
|
||||
// Changelog
|
||||
UIFeeder::Add(62.0f, Changelog::GetChangelogCount, Changelog::GetChangelogText, Changelog::SelectChangelog);
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -55,16 +55,16 @@ namespace Components
|
||||
push eax
|
||||
pushad
|
||||
|
||||
push[esp + 100h + 28h]
|
||||
push [esp + 100h + 28h]
|
||||
push eax
|
||||
call Chat::EvaluateSay
|
||||
add esp, 8h
|
||||
|
||||
mov[esp + 20h], eax
|
||||
mov [esp + 20h], eax
|
||||
popad
|
||||
pop eax
|
||||
|
||||
mov[esp + 100h + 10h], eax
|
||||
mov [esp + 100h + 10h], eax
|
||||
|
||||
jmp PlayerName::CleanStrStub
|
||||
}
|
||||
@ -97,7 +97,7 @@ namespace Components
|
||||
}
|
||||
|
||||
void Chat::CheckChatLineEnd(const char*& inputBuffer, char*& lineBuffer, float& len, const int chatHeight, const float chatWidth, char*& lastSpacePos, char*& lastFontIconPos, const int lastColor)
|
||||
{
|
||||
{
|
||||
if (len > chatWidth)
|
||||
{
|
||||
if (lastSpacePos && lastSpacePos > lastFontIconPos)
|
||||
@ -110,6 +110,7 @@ namespace Components
|
||||
inputBuffer += lastFontIconPos - lineBuffer;
|
||||
lineBuffer = lastFontIconPos;
|
||||
}
|
||||
|
||||
*lineBuffer = 0;
|
||||
len = 0.0f;
|
||||
Game::cgsArray[0].teamChatMsgTimes[Game::cgsArray[0].teamChatPos % chatHeight] = Game::cgArray[0].time;
|
||||
@ -122,7 +123,7 @@ namespace Components
|
||||
lastSpacePos = nullptr;
|
||||
lastFontIconPos = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chat::CG_AddToTeamChat(const char* text)
|
||||
{
|
||||
@ -132,7 +133,7 @@ namespace Components
|
||||
const auto chatHeight = (*cg_chatHeight)->current.integer;
|
||||
const auto chatWidth = static_cast<float>(cg_chatWidth.get<int>());
|
||||
const auto chatTime = (*cg_chatTime)->current.integer;
|
||||
if (chatHeight < 0 || static_cast<unsigned>(chatHeight) > std::extent_v<decltype(Game::cgs_t::teamChatMsgs)> || chatWidth <= 0 || chatTime <= 0)
|
||||
if (chatHeight <= 0 || static_cast<unsigned>(chatHeight) > std::extent_v<decltype(Game::cgs_t::teamChatMsgs)> || chatWidth <= 0 || chatTime <= 0)
|
||||
{
|
||||
Game::cgsArray[0].teamLastChatPos = 0;
|
||||
Game::cgsArray[0].teamChatPos = 0;
|
||||
@ -152,7 +153,7 @@ namespace Components
|
||||
CheckChatLineEnd(text, p, len, chatHeight, chatWidth, lastSpace, lastFontIcon, lastColor);
|
||||
|
||||
const char* fontIconEndPos = &text[1];
|
||||
if(text[0] == TextRenderer::FONT_ICON_SEPARATOR_CHARACTER && TextRenderer::IsFontIcon(fontIconEndPos, fontIconInfo))
|
||||
if (text[0] == TextRenderer::FONT_ICON_SEPARATOR_CHARACTER && TextRenderer::IsFontIcon(fontIconEndPos, fontIconInfo))
|
||||
{
|
||||
// The game calculates width on a per character base. Since the width of a font icon is calculated based on the height of the font
|
||||
// which is roughly double as much as the average width of a character without an additional multiplier the calculated len of the font icon
|
||||
|
@ -1,176 +0,0 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
void Client::AddFunctions()
|
||||
{
|
||||
//File functions
|
||||
|
||||
Script::AddFunction("fileWrite", [](Game::scr_entref_t) // gsc: fileWrite(<filepath>, <string>, <mode>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
auto text = Game::Scr_GetString(1);
|
||||
auto mode = Game::Scr_GetString(2);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileWrite: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileWrite: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != "append"s && mode != "write"s)
|
||||
{
|
||||
Game::Com_Printf(0, "^3fileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||
mode = const_cast<char*>("write");
|
||||
}
|
||||
|
||||
if (mode == "write"s)
|
||||
{
|
||||
FileSystem::FileWriter(path).write(text);
|
||||
}
|
||||
else if (mode == "append"s)
|
||||
{
|
||||
FileSystem::FileWriter(path, true).write(text);
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("fileRead", [](Game::scr_entref_t) // gsc: fileRead(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileSystem::FileReader(path).exists())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: file not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
|
||||
});
|
||||
|
||||
Script::AddFunction("fileExists", [](Game::scr_entref_t) // gsc: fileExists(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileExists: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileExists: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
|
||||
});
|
||||
|
||||
Script::AddFunction("fileRemove", [](Game::scr_entref_t) // gsc: fileRemove(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRemove: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRemove: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto p = std::filesystem::path(path);
|
||||
std::string folder = p.parent_path().string();
|
||||
std::string file = p.filename().string();
|
||||
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
|
||||
});
|
||||
}
|
||||
|
||||
void Client::AddMethods()
|
||||
{
|
||||
// Client methods
|
||||
|
||||
Script::AddFunction("getIp", [](Game::scr_entref_t id) // gsc: self getIp()
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
|
||||
if (client->state >= 3)
|
||||
{
|
||||
std::string ip = Game::NET_AdrToString(client->netchan.remoteAddress);
|
||||
if (ip.find_first_of(":") != std::string::npos)
|
||||
ip.erase(ip.begin() + ip.find_first_of(":"), ip.end()); // erase port
|
||||
Game::Scr_AddString(ip.data());
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("getPing", [](Game::scr_entref_t id) // gsc: self getPing()
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
|
||||
if (client->state >= 3)
|
||||
{
|
||||
int ping = (int)client->ping;
|
||||
Game::Scr_AddInt(ping);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Client::AddCommands()
|
||||
{
|
||||
Command::Add("NULL", [](Command::Params*)
|
||||
{
|
||||
return NULL;
|
||||
});
|
||||
}
|
||||
|
||||
Client::Client()
|
||||
{
|
||||
Client::AddFunctions();
|
||||
Client::AddMethods();
|
||||
Client::AddCommands();
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Client : public Component
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
~Client();
|
||||
|
||||
private:
|
||||
|
||||
static void AddFunctions();
|
||||
static void AddMethods();
|
||||
static void AddCommands();
|
||||
};
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<std::string, Utils::Slot<ClientCommand::Callback>> ClientCommand::FunctionMap;
|
||||
std::unordered_map<std::string, std::function<void(Game::gentity_s*, Command::ServerParams*)>> ClientCommand::HandlersSV;
|
||||
|
||||
bool ClientCommand::CheatsOk(const Game::gentity_s* ent)
|
||||
{
|
||||
@ -17,7 +17,7 @@ namespace Components
|
||||
|
||||
if (ent->health < 1)
|
||||
{
|
||||
Logger::Print("CheatsOk: entity %u must be alive to use this command!\n", entNum);
|
||||
Logger::Print("CheatsOk: entity %i must be alive to use this command!\n", entNum);
|
||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
||||
return false;
|
||||
}
|
||||
@ -25,49 +25,38 @@ namespace Components
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClientCommand::CallbackHandler(Game::gentity_s* ent, const char* cmd)
|
||||
{
|
||||
const auto command = Utils::String::ToLower(cmd);
|
||||
|
||||
if (ClientCommand::FunctionMap.find(command) != ClientCommand::FunctionMap.end())
|
||||
{
|
||||
ClientCommand::FunctionMap[command](ent);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientCommand::Add(const char* name, Utils::Slot<Callback> callback)
|
||||
void ClientCommand::Add(const char* name, std::function<void(Game::gentity_s*, Command::ServerParams*)> callback)
|
||||
{
|
||||
const auto command = Utils::String::ToLower(name);
|
||||
|
||||
ClientCommand::FunctionMap[command] = callback;
|
||||
ClientCommand::HandlersSV[command] = std::move(callback);
|
||||
}
|
||||
|
||||
void ClientCommand::ClientCommandStub(const int clientNum)
|
||||
{
|
||||
char cmd[1024]{};
|
||||
const auto entity = &Game::g_entities[clientNum];
|
||||
const auto ent = &Game::g_entities[clientNum];
|
||||
|
||||
if (entity->client == nullptr)
|
||||
if (ent->client == nullptr)
|
||||
{
|
||||
Logger::Print("ClientCommand: client %d is not fully in game yet\n", clientNum);
|
||||
return;
|
||||
}
|
||||
|
||||
Game::SV_Cmd_ArgvBuffer(0, cmd, sizeof(cmd));
|
||||
Command::ServerParams params;
|
||||
const auto command = Utils::String::ToLower(params.get(0));
|
||||
|
||||
if (!ClientCommand::CallbackHandler(entity, cmd))
|
||||
if (const auto got = HandlersSV.find(command); got != HandlersSV.end())
|
||||
{
|
||||
// If no callback was found call original game function
|
||||
Utils::Hook::Call<void(const int)>(0x416790)(clientNum);
|
||||
got->second(ent, ¶ms);
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void(const int)>(0x416790)(clientNum);
|
||||
}
|
||||
|
||||
void ClientCommand::AddCheatCommands()
|
||||
{
|
||||
ClientCommand::Add("noclip", [](Game::gentity_s* ent)
|
||||
ClientCommand::Add("noclip", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (!ClientCommand::CheatsOk(ent))
|
||||
return;
|
||||
@ -75,13 +64,13 @@ namespace Components
|
||||
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||
|
||||
const auto entNum = ent->s.number;
|
||||
Logger::Print("Noclip toggled for entity %u\n", entNum);
|
||||
Logger::Print("Noclip toggled for entity %i\n", entNum);
|
||||
|
||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||
});
|
||||
|
||||
ClientCommand::Add("ufo", [](Game::gentity_s* ent)
|
||||
ClientCommand::Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (!ClientCommand::CheatsOk(ent))
|
||||
return;
|
||||
@ -89,13 +78,13 @@ namespace Components
|
||||
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||
|
||||
const auto entNum = ent->s.number;
|
||||
Logger::Print("UFO toggled for entity %u\n", entNum);
|
||||
Logger::Print("UFO toggled for entity %i\n", entNum);
|
||||
|
||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||
});
|
||||
|
||||
ClientCommand::Add("god", [](Game::gentity_s* ent)
|
||||
ClientCommand::Add("god", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (!ClientCommand::CheatsOk(ent))
|
||||
return;
|
||||
@ -103,13 +92,13 @@ namespace Components
|
||||
ent->flags ^= Game::FL_GODMODE;
|
||||
|
||||
const auto entNum = ent->s.number;
|
||||
Logger::Print("God toggled for entity %u\n", entNum);
|
||||
Logger::Print("God toggled for entity %i\n", entNum);
|
||||
|
||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||
});
|
||||
|
||||
ClientCommand::Add("demigod", [](Game::gentity_s* ent)
|
||||
ClientCommand::Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (!ClientCommand::CheatsOk(ent))
|
||||
return;
|
||||
@ -117,13 +106,13 @@ namespace Components
|
||||
ent->flags ^= Game::FL_DEMI_GODMODE;
|
||||
|
||||
const auto entNum = ent->s.number;
|
||||
Logger::Print("Demigod toggled for entity %u\n", entNum);
|
||||
Logger::Print("Demigod toggled for entity %i\n", entNum);
|
||||
|
||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
||||
});
|
||||
|
||||
ClientCommand::Add("notarget", [](Game::gentity_s* ent)
|
||||
ClientCommand::Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (!ClientCommand::CheatsOk(ent))
|
||||
return;
|
||||
@ -131,23 +120,22 @@ namespace Components
|
||||
ent->flags ^= Game::FL_NOTARGET;
|
||||
|
||||
const auto entNum = ent->s.number;
|
||||
Logger::Print("Notarget toggled for entity %u\n", entNum);
|
||||
Logger::Print("Notarget toggled for entity %i\n", entNum);
|
||||
|
||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
||||
});
|
||||
|
||||
ClientCommand::Add("setviewpos", [](Game::gentity_s* ent)
|
||||
ClientCommand::Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
assert(ent != nullptr);
|
||||
|
||||
if (!ClientCommand::CheatsOk(ent))
|
||||
return;
|
||||
|
||||
Command::ServerParams params = {};
|
||||
Game::vec3_t origin, angles{0.f, 0.f, 0.f};
|
||||
|
||||
if (params.size() < 4 || params.size() > 6)
|
||||
if (params->size() < 4 || params->size() > 6)
|
||||
{
|
||||
Game::SV_GameSendServerCommand(ent->s.number, 0,
|
||||
Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
|
||||
@ -156,149 +144,220 @@ namespace Components
|
||||
|
||||
for (auto i = 0; i < 3; i++)
|
||||
{
|
||||
origin[i] = std::strtof(params.get(i + 1), nullptr);
|
||||
origin[i] = std::strtof(params->get(i + 1), nullptr);
|
||||
}
|
||||
|
||||
if (params.size() >= 5)
|
||||
if (params->size() >= 5)
|
||||
{
|
||||
angles[1] = std::strtof(params.get(4), nullptr); // Yaw
|
||||
angles[1] = std::strtof(params->get(4), nullptr); // Yaw
|
||||
}
|
||||
|
||||
if (params.size() == 6)
|
||||
if (params->size() == 6)
|
||||
{
|
||||
angles[0] = std::strtof(params.get(5), nullptr); // Pitch
|
||||
angles[0] = std::strtof(params->get(5), nullptr); // Pitch
|
||||
}
|
||||
|
||||
Game::TeleportPlayer(ent, origin, angles);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientCommand::AddDevelopmentCommands()
|
||||
{
|
||||
ClientCommand::Add("dropallbots", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
Game::SV_DropAllBots();
|
||||
});
|
||||
|
||||
ClientCommand::Add("entitylist", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
Game::Svcmd_EntityList_f();
|
||||
});
|
||||
|
||||
ClientCommand::Add("printentities", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
Game::G_PrintEntities();
|
||||
});
|
||||
|
||||
ClientCommand::Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
Logger::Print("Entity count = %i\n", Game::level->num_entities);
|
||||
});
|
||||
|
||||
// Also known as: "vis"
|
||||
ClientCommand::Add("visionsetnaked", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (params->size() < 2)
|
||||
{
|
||||
Logger::Print("USAGE: visionSetNaked <name> <duration>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto duration = 1000;
|
||||
if (params->size() > 2)
|
||||
{
|
||||
const auto input = std::strtof(params->get(2), nullptr);
|
||||
duration = static_cast<int>(std::floorf(input * 1000.0f + 0.5f));
|
||||
}
|
||||
|
||||
assert(ent->client != nullptr);
|
||||
|
||||
constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NORMAL;
|
||||
const auto* name = params->get(1);
|
||||
|
||||
ent->client->visionDuration[visMode] = duration;
|
||||
strncpy_s(ent->client->visionName[visMode],
|
||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||
|
||||
Game::SV_GameSendServerCommand(ent->s.number, 1,
|
||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||
});
|
||||
|
||||
ClientCommand::Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
if (params->size() < 2)
|
||||
{
|
||||
Logger::Print("USAGE: visionSetNight <name> <duration>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto duration = 1000;
|
||||
if (params->size() > 2)
|
||||
{
|
||||
const auto input = std::strtof(params->get(2), nullptr);
|
||||
duration = static_cast<int>(std::floorf(input * 1000.0f + 0.5f));
|
||||
}
|
||||
|
||||
assert(ent->client != nullptr);
|
||||
|
||||
constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NIGHT;
|
||||
const auto* name = params->get(1);
|
||||
|
||||
ent->client->visionDuration[visMode] = duration;
|
||||
strncpy_s(ent->client->visionName[visMode],
|
||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||
|
||||
Game::SV_GameSendServerCommand(ent->s.number, 1,
|
||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||
});
|
||||
|
||||
ClientCommand::Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||
{
|
||||
assert(ent != nullptr);
|
||||
ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time
|
||||
});
|
||||
}
|
||||
|
||||
void ClientCommand::AddScriptFunctions()
|
||||
{
|
||||
Script::AddFunction("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(<optional int toggle>);
|
||||
Script::AddMethod("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(<optional int toggle>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1NoClip: entity %u is not a client\n", entref));
|
||||
return;
|
||||
}
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_NOCLIP;
|
||||
ent->client->flags |= Game::PLAYER_FLAG_NOCLIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
|
||||
ent->client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(<optional int toggle>);
|
||||
Script::AddMethod("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(<optional int toggle>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1Ufo: entity %u is not a client\n", entref));
|
||||
return;
|
||||
}
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_UFO;
|
||||
ent->client->flags |= Game::PLAYER_FLAG_UFO;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_UFO;
|
||||
ent->client->flags &= ~Game::PLAYER_FLAG_UFO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("God", [](Game::scr_entref_t entref) // gsc: God(<optional int toggle>);
|
||||
Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: God(<optional int toggle>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1God: entity %u is out of bounds\n", entref));
|
||||
return;
|
||||
}
|
||||
auto* ent = Game::GetEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].flags |= Game::FL_GODMODE;
|
||||
ent->flags |= Game::FL_GODMODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags &= ~Game::FL_GODMODE;
|
||||
ent->flags &= ~Game::FL_GODMODE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags ^= Game::FL_GODMODE;
|
||||
ent->flags ^= Game::FL_GODMODE;
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(<optional int toggle>);
|
||||
Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(<optional int toggle>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1Demigod: entity %u is out of bounds\n", entref));
|
||||
return;
|
||||
}
|
||||
auto* ent = Game::GetEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].flags |= Game::FL_DEMI_GODMODE;
|
||||
ent->flags |= Game::FL_DEMI_GODMODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags &= ~Game::FL_DEMI_GODMODE;
|
||||
ent->flags &= ~Game::FL_DEMI_GODMODE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags ^= Game::FL_DEMI_GODMODE;
|
||||
ent->flags ^= Game::FL_DEMI_GODMODE;
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(<optional int toggle>);
|
||||
Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(<optional int toggle>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1Notarget: entity %u is out of bounds\n", entref));
|
||||
return;
|
||||
}
|
||||
auto* ent = Game::GetEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].flags |= Game::FL_NOTARGET;
|
||||
ent->flags |= Game::FL_NOTARGET;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags &= ~Game::FL_NOTARGET;
|
||||
ent->flags &= ~Game::FL_NOTARGET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags ^= Game::FL_NOTARGET;
|
||||
ent->flags ^= Game::FL_NOTARGET;
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("DropAllBots", []() // gsc: DropAllBots();
|
||||
{
|
||||
Game::SV_DropAllBots();
|
||||
});
|
||||
}
|
||||
|
||||
ClientCommand::ClientCommand()
|
||||
@ -308,5 +367,8 @@ namespace Components
|
||||
|
||||
ClientCommand::AddCheatCommands();
|
||||
ClientCommand::AddScriptFunctions();
|
||||
#ifdef _DEBUG
|
||||
ClientCommand::AddDevelopmentCommands();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -5,19 +5,17 @@ namespace Components
|
||||
class ClientCommand : public Component
|
||||
{
|
||||
public:
|
||||
typedef void(Callback)(Game::gentity_s* entity);
|
||||
|
||||
ClientCommand();
|
||||
|
||||
static void Add(const char* name, Utils::Slot<Callback> callback);
|
||||
static void Add(const char* name, std::function<void(Game::gentity_s*, Command::ServerParams*)> callback);
|
||||
static bool CheatsOk(const Game::gentity_s* ent);
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, Utils::Slot<Callback>> FunctionMap;
|
||||
static std::unordered_map<std::string, std::function<void(Game::gentity_s*, Command::ServerParams*)>> HandlersSV;
|
||||
|
||||
static bool CallbackHandler(Game::gentity_s* ent, const char* cmd);
|
||||
static void ClientCommandStub(const int clientNum);
|
||||
static void AddCheatCommands();
|
||||
static void AddDevelopmentCommands();
|
||||
static void AddScriptFunctions();
|
||||
};
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<std::string, Utils::Slot<Command::Callback>> Command::FunctionMap;
|
||||
std::unordered_map<std::string, Utils::Slot<Command::Callback>> Command::FunctionMapSV;
|
||||
std::unordered_map<std::string, std::function<void(Command::Params*)>> Command::FunctionMap;
|
||||
std::unordered_map<std::string, std::function<void(Command::Params*)>> Command::FunctionMapSV;
|
||||
|
||||
std::string Command::Params::join(const int index)
|
||||
{
|
||||
@ -60,7 +60,7 @@ namespace Components
|
||||
return Game::sv_cmd_args->argv[this->nesting_][index];
|
||||
}
|
||||
|
||||
void Command::Add(const char* name, Utils::Slot<Command::Callback> callback)
|
||||
void Command::Add(const char* name, std::function<void(Command::Params*)> callback)
|
||||
{
|
||||
const auto command = Utils::String::ToLower(name);
|
||||
|
||||
@ -69,10 +69,10 @@ namespace Components
|
||||
Command::AddRaw(name, Command::MainCallback);
|
||||
}
|
||||
|
||||
Command::FunctionMap[command] = std::move(callback);
|
||||
Command::FunctionMap.insert_or_assign(command, std::move(callback));
|
||||
}
|
||||
|
||||
void Command::AddSV(const char* name, Utils::Slot<Command::Callback> callback)
|
||||
void Command::AddSV(const char* name, std::function<void(Command::Params*)> callback)
|
||||
{
|
||||
if (Loader::IsPregame())
|
||||
{
|
||||
@ -95,7 +95,7 @@ namespace Components
|
||||
Command::AddRaw(name, Game::Cbuf_AddServerText);
|
||||
}
|
||||
|
||||
FunctionMapSV[command] = std::move(callback);
|
||||
FunctionMapSV.insert_or_assign(command, std::move(callback));
|
||||
}
|
||||
|
||||
void Command::AddRaw(const char* name, void(*callback)(), bool key)
|
||||
@ -127,11 +127,11 @@ namespace Components
|
||||
|
||||
Game::cmd_function_t* Command::Find(const std::string& command)
|
||||
{
|
||||
Game::cmd_function_t* cmdFunction = *Game::cmd_functions;
|
||||
auto* cmdFunction = *Game::cmd_functions;
|
||||
|
||||
while (cmdFunction)
|
||||
while (cmdFunction != nullptr)
|
||||
{
|
||||
if (cmdFunction->name && cmdFunction->name == command)
|
||||
if (cmdFunction->name != nullptr && cmdFunction->name == command)
|
||||
{
|
||||
return cmdFunction;
|
||||
}
|
||||
@ -149,12 +149,10 @@ namespace Components
|
||||
|
||||
void Command::MainCallback()
|
||||
{
|
||||
Command::ClientParams params;
|
||||
|
||||
ClientParams params;
|
||||
const auto command = Utils::String::ToLower(params[0]);
|
||||
const auto got = Command::FunctionMap.find(command);
|
||||
|
||||
if (got != Command::FunctionMap.end())
|
||||
if (const auto got = FunctionMap.find(command); got != FunctionMap.end())
|
||||
{
|
||||
got->second(¶ms);
|
||||
}
|
||||
@ -162,12 +160,10 @@ namespace Components
|
||||
|
||||
void Command::MainCallbackSV()
|
||||
{
|
||||
Command::ServerParams params;
|
||||
|
||||
ServerParams params;
|
||||
const auto command = Utils::String::ToLower(params[0]);
|
||||
const auto got = Command::FunctionMapSV.find(command);
|
||||
|
||||
if (got != Command::FunctionMapSV.end())
|
||||
if (const auto got = FunctionMapSV.find(command); got != FunctionMapSV.end())
|
||||
{
|
||||
got->second(¶ms);
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ namespace Components
|
||||
class Params
|
||||
{
|
||||
public:
|
||||
Params() {};
|
||||
Params() = default;
|
||||
virtual ~Params() = default;
|
||||
|
||||
virtual int size() = 0;
|
||||
virtual const char* get(int index) = 0;
|
||||
@ -20,7 +21,7 @@ namespace Components
|
||||
}
|
||||
};
|
||||
|
||||
class ClientParams : public Params
|
||||
class ClientParams final : public Params
|
||||
{
|
||||
public:
|
||||
ClientParams();
|
||||
@ -32,7 +33,7 @@ namespace Components
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
class ServerParams : public Params
|
||||
class ServerParams final : public Params
|
||||
{
|
||||
public:
|
||||
ServerParams();
|
||||
@ -44,14 +45,12 @@ namespace Components
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
typedef void(Callback)(Command::Params* params);
|
||||
|
||||
Command();
|
||||
|
||||
static Game::cmd_function_t* Allocate();
|
||||
|
||||
static void Add(const char* name, Utils::Slot<Callback> callback);
|
||||
static void AddSV(const char* name, Utils::Slot<Callback> callback);
|
||||
static void Add(const char* name, std::function<void(Command::Params*)> callback);
|
||||
static void AddSV(const char* name, std::function<void(Command::Params*)> callback);
|
||||
static void AddRaw(const char* name, void(*callback)(), bool key = false);
|
||||
static void AddRawSV(const char* name, void(*callback)());
|
||||
static void Execute(std::string command, bool sync = true);
|
||||
@ -59,8 +58,8 @@ namespace Components
|
||||
static Game::cmd_function_t* Find(const std::string& command);
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, Utils::Slot<Callback>> FunctionMap;
|
||||
static std::unordered_map<std::string, Utils::Slot<Callback>> FunctionMapSV;
|
||||
static std::unordered_map<std::string, std::function<void(Command::Params*)>> FunctionMap;
|
||||
static std::unordered_map<std::string, std::function<void(Command::Params*)>> FunctionMapSV;
|
||||
|
||||
static void MainCallback();
|
||||
static void MainCallbackSV();
|
||||
|
@ -51,8 +51,8 @@ namespace Components
|
||||
{
|
||||
SetConsoleTitleA(hostname.data());
|
||||
|
||||
int clientCount = 0;
|
||||
int maxclientCount = *Game::svs_numclients;
|
||||
auto clientCount = 0;
|
||||
auto maxclientCount = *Game::svs_clientCount;
|
||||
|
||||
if (maxclientCount)
|
||||
{
|
||||
|
@ -374,7 +374,6 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef USE_LEGACY_SERVER_LIST
|
||||
// Heartbeats
|
||||
Scheduler::Once(Dedicated::Heartbeat);
|
||||
Scheduler::OnFrame([]()
|
||||
@ -387,7 +386,6 @@ namespace Components
|
||||
Dedicated::Heartbeat();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
|
@ -83,16 +83,6 @@ namespace Components
|
||||
ServerList::InsertRequest(address);
|
||||
}
|
||||
});
|
||||
|
||||
// This is placed here in case the anticheat has been disabled!
|
||||
// Make sure this is called after the memory scan!
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Utils::Hook(0x5ACB9E, []() // Somewhere in the renderer, past the scan check
|
||||
{
|
||||
AntiCheat::ScanIntegrityCheck();
|
||||
return Utils::Hook::Call<void()>(0x4AA720)();
|
||||
}, HOOK_CALL).install()->quick();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Discovery::preDestroy()
|
||||
|
@ -381,7 +381,7 @@ namespace Components
|
||||
{
|
||||
Network::Address address(nc->sa.sa);
|
||||
|
||||
for (int i = 0; i < *Game::svs_numclients; ++i)
|
||||
for (int i = 0; i < *Game::svs_clientCount; ++i)
|
||||
{
|
||||
Game::client_t* client = &Game::svs_clients[i];
|
||||
|
||||
@ -964,13 +964,20 @@ namespace Components
|
||||
Download::ScriptDownloads.clear();
|
||||
});
|
||||
|
||||
Script::AddFunction("httpGet", [](Game::scr_entref_t)
|
||||
Script::AddFunction("HttpGet", []()
|
||||
{
|
||||
if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return;
|
||||
if (Game::Scr_GetNumParam() < 1u) return;
|
||||
if (!Flags::HasFlag("scriptablehttp"))
|
||||
return;
|
||||
|
||||
std::string url = Game::Scr_GetString(0);
|
||||
unsigned int object = Game::AllocObject();
|
||||
const auto* url = Game::Scr_GetString(0);
|
||||
|
||||
if (url == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1HttpGet: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto object = Game::AllocObject();
|
||||
|
||||
Game::Scr_AddObject(object);
|
||||
|
||||
@ -978,13 +985,13 @@ namespace Components
|
||||
Game::RemoveRefToObject(object);
|
||||
});
|
||||
|
||||
Script::AddFunction("httpCancel", [](Game::scr_entref_t)
|
||||
Script::AddFunction("HttpCancel", []()
|
||||
{
|
||||
if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return;
|
||||
if (Game::Scr_GetNumParam() < 1u) return;
|
||||
if (!Flags::HasFlag("scriptablehttp"))
|
||||
return;
|
||||
|
||||
unsigned int object = Game::Scr_GetObject(0);
|
||||
for (auto& download : Download::ScriptDownloads)
|
||||
const auto object = Game::Scr_GetObject(0);
|
||||
for (const auto& download : Download::ScriptDownloads)
|
||||
{
|
||||
if (object == download->getObject())
|
||||
{
|
||||
|
@ -137,10 +137,6 @@ namespace Components
|
||||
Utils::IO::CreateDir("minidumps");
|
||||
PathCombineA(filename, "minidumps\\", Utils::String::VA("%s-" VERSION "-%s.dmp", exeFileName, filenameFriendlyTime));
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
AntiCheat::UninstallLibHook();
|
||||
#endif
|
||||
|
||||
DWORD fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE | GENERIC_READ, fileShare, nullptr, (fileShare & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, nullptr);
|
||||
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE };
|
||||
@ -157,10 +153,6 @@ namespace Components
|
||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
AntiCheat::InstallLibHook();
|
||||
#endif
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace Components
|
||||
if (!rawfile || Game::DB_IsXAssetDefault(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data())) return;
|
||||
|
||||
this->buffer.resize(Game::DB_GetRawFileLen(rawfile));
|
||||
Game::DB_GetRawBuffer(rawfile, const_cast<char*>(this->buffer.data()), this->buffer.size());
|
||||
Game::DB_GetRawBuffer(rawfile, this->buffer.data(), static_cast<int>(this->buffer.size()));
|
||||
}
|
||||
|
||||
FileSystem::FileReader::FileReader(const std::string& file) : handle(0), name(file)
|
||||
|
@ -11,6 +11,10 @@ namespace Components
|
||||
std::recursive_mutex Friends::Mutex;
|
||||
std::vector<Friends::Friend> Friends::FriendsList;
|
||||
|
||||
Dvar::Var Friends::UIStreamFriendly;
|
||||
Dvar::Var Friends::CLAnonymous;
|
||||
Dvar::Var Friends::CLNotifyFriendState;
|
||||
|
||||
void Friends::SortIndividualList(std::vector<Friends::Friend>* list)
|
||||
{
|
||||
std::stable_sort(list->begin(), list->end(), [](Friends::Friend const& friend1, Friends::Friend const& friend2)
|
||||
@ -111,8 +115,8 @@ namespace Components
|
||||
|
||||
Friends::SortList();
|
||||
|
||||
const auto notify = Dvar::Var("cl_notifyFriendState").get<bool>();
|
||||
if (gotOnline && (!notify || (notify && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get<bool>())
|
||||
const auto notify = Friends::CLNotifyFriendState.get<bool>();
|
||||
if (gotOnline && (!notify || (notify && !Game::CL_IsCgameInitialized())) && !Friends::UIStreamFriendly.get<bool>())
|
||||
{
|
||||
Game::Material* material = Friends::CreateAvatar(user);
|
||||
Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]()
|
||||
@ -124,7 +128,7 @@ namespace Components
|
||||
|
||||
void Friends::UpdateState(bool force)
|
||||
{
|
||||
if (Dvar::Var("cl_anonymous").get<bool>() || Friends::IsInvisible() || !Steam::Enabled()) return;
|
||||
if (Friends::CLAnonymous.get<bool>() || Friends::IsInvisible() || !Steam::Enabled()) return;
|
||||
|
||||
if (force)
|
||||
{
|
||||
@ -228,7 +232,7 @@ namespace Components
|
||||
|
||||
void Friends::SetPresence(const std::string& key, const std::string& value)
|
||||
{
|
||||
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamUtils && !Dvar::Var("cl_anonymous").get<bool>() && !Friends::IsInvisible() && Steam::Enabled())
|
||||
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamUtils && !Friends::CLAnonymous.get<bool>() && !Friends::IsInvisible() && Steam::Enabled())
|
||||
{
|
||||
Friends::SetRawPresence(key.data(), value.data());
|
||||
}
|
||||
@ -576,10 +580,15 @@ namespace Components
|
||||
{
|
||||
Friends::LoggedOn = false;
|
||||
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled()) return;
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled())
|
||||
return;
|
||||
|
||||
Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_ARCHIVE, "Enable invisible mode for Steam");
|
||||
Dvar::Register<bool>("cl_notifyFriendState", true, Game::DVAR_ARCHIVE, "Update friends about current game status");
|
||||
Dvar::OnInit([]
|
||||
{
|
||||
Friends::UIStreamFriendly = Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_ARCHIVE, "Stream friendly UI");
|
||||
Friends::CLAnonymous = Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_ARCHIVE, "Enable invisible mode for Steam");
|
||||
Friends::CLNotifyFriendState = Dvar::Register<bool>("cl_notifyFriendState", true, Game::DVAR_ARCHIVE, "Update friends about current game status");
|
||||
});
|
||||
|
||||
Command::Add("addFriend", [](Command::Params* params)
|
||||
{
|
||||
@ -712,11 +721,11 @@ namespace Components
|
||||
Friends::InitialState = Steam::Proxy::SteamFriends->GetFriendPersonaState(Steam::Proxy::SteamUser_->GetSteamID());
|
||||
}
|
||||
|
||||
if (Dvar::Var("cl_anonymous").get<bool>() || Friends::IsInvisible() || !Steam::Enabled())
|
||||
if (Friends::CLAnonymous.get<bool>() || Friends::IsInvisible() || !Steam::Enabled())
|
||||
{
|
||||
if (Steam::Proxy::ClientFriends)
|
||||
{
|
||||
for (auto id : Friends::GetAppIdList())
|
||||
for (const auto id : Friends::GetAppIdList())
|
||||
{
|
||||
Steam::Proxy::ClientFriends.invoke<void>("ClearRichPresence", id);
|
||||
}
|
||||
|
@ -25,6 +25,10 @@ namespace Components
|
||||
|
||||
static bool IsInvisible();
|
||||
|
||||
static Dvar::Var UIStreamFriendly;
|
||||
static Dvar::Var CLAnonymous;
|
||||
static Dvar::Var CLNotifyFriendState;
|
||||
|
||||
private:
|
||||
#pragma pack(push, 4)
|
||||
struct FriendRichPresenceUpdate
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,202 +2,202 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Gamepad : public Component
|
||||
{
|
||||
struct ControllerMenuKeyMapping
|
||||
{
|
||||
Game::keyNum_t controllerKey;
|
||||
Game::keyNum_t pcKey;
|
||||
};
|
||||
class Gamepad : public Component
|
||||
{
|
||||
struct ControllerMenuKeyMapping
|
||||
{
|
||||
Game::keyNum_t controllerKey;
|
||||
Game::keyNum_t pcKey;
|
||||
};
|
||||
|
||||
struct GamePad
|
||||
{
|
||||
bool enabled;
|
||||
bool inUse;
|
||||
int portIndex;
|
||||
unsigned short digitals;
|
||||
unsigned short lastDigitals;
|
||||
float analogs[2];
|
||||
float lastAnalogs[2];
|
||||
float sticks[4];
|
||||
float lastSticks[4];
|
||||
bool stickDown[4][Game::GPAD_STICK_DIR_COUNT];
|
||||
bool stickDownLast[4][Game::GPAD_STICK_DIR_COUNT];
|
||||
float lowRumble;
|
||||
float highRumble;
|
||||
struct GamePad
|
||||
{
|
||||
bool enabled;
|
||||
bool inUse;
|
||||
int portIndex;
|
||||
unsigned short digitals;
|
||||
unsigned short lastDigitals;
|
||||
float analogs[2];
|
||||
float lastAnalogs[2];
|
||||
float sticks[4];
|
||||
float lastSticks[4];
|
||||
bool stickDown[4][Game::GPAD_STICK_DIR_COUNT];
|
||||
bool stickDownLast[4][Game::GPAD_STICK_DIR_COUNT];
|
||||
float lowRumble;
|
||||
float highRumble;
|
||||
|
||||
XINPUT_VIBRATION rumble;
|
||||
XINPUT_CAPABILITIES caps;
|
||||
};
|
||||
XINPUT_VIBRATION rumble;
|
||||
XINPUT_CAPABILITIES caps;
|
||||
};
|
||||
|
||||
struct GamePadGlobals
|
||||
{
|
||||
Game::GpadAxesGlob axes;
|
||||
unsigned nextScrollTime;
|
||||
struct GamePadGlobals
|
||||
{
|
||||
Game::GpadAxesGlob axes;
|
||||
unsigned nextScrollTime;
|
||||
|
||||
GamePadGlobals();
|
||||
};
|
||||
GamePadGlobals();
|
||||
};
|
||||
|
||||
public:
|
||||
Gamepad();
|
||||
public:
|
||||
Gamepad();
|
||||
|
||||
private:
|
||||
static Game::ButtonToCodeMap_t buttonList[];
|
||||
static Game::StickToCodeMap_t analogStickList[4];
|
||||
static Game::GamePadStick stickForAxis[];
|
||||
static Game::GamepadPhysicalAxis axisSameStick[];
|
||||
static const char* physicalAxisNames[];
|
||||
static const char* virtualAxisNames[];
|
||||
static const char* gamePadMappingTypeNames[];
|
||||
static Game::keyNum_t menuScrollButtonList[];
|
||||
static Game::keyname_t extendedKeyNames[];
|
||||
static Game::keyname_t extendedLocalizedKeyNamesXenon[];
|
||||
static Game::keyname_t extendedLocalizedKeyNamesPs3[];
|
||||
static Game::keyname_t combinedKeyNames[];
|
||||
static Game::keyname_t combinedLocalizedKeyNamesXenon[];
|
||||
static Game::keyname_t combinedLocalizedKeyNamesPs3[];
|
||||
static ControllerMenuKeyMapping controllerMenuKeyMappings[];
|
||||
private:
|
||||
static Game::ButtonToCodeMap_t buttonList[];
|
||||
static Game::StickToCodeMap_t analogStickList[4];
|
||||
static Game::GamePadStick stickForAxis[];
|
||||
static Game::GamepadPhysicalAxis axisSameStick[];
|
||||
static const char* physicalAxisNames[];
|
||||
static const char* virtualAxisNames[];
|
||||
static const char* gamePadMappingTypeNames[];
|
||||
static Game::keyNum_t menuScrollButtonList[];
|
||||
static Game::keyname_t extendedKeyNames[];
|
||||
static Game::keyname_t extendedLocalizedKeyNamesXenon[];
|
||||
static Game::keyname_t extendedLocalizedKeyNamesPs3[];
|
||||
static Game::keyname_t combinedKeyNames[];
|
||||
static Game::keyname_t combinedLocalizedKeyNamesXenon[];
|
||||
static Game::keyname_t combinedLocalizedKeyNamesPs3[];
|
||||
static ControllerMenuKeyMapping controllerMenuKeyMappings[];
|
||||
|
||||
static GamePad gamePads[Game::MAX_GAMEPADS];
|
||||
static GamePadGlobals gamePadGlobals[Game::MAX_GAMEPADS];
|
||||
static GamePad gamePads[Game::MAX_GAMEPADS];
|
||||
static GamePadGlobals gamePadGlobals[Game::MAX_GAMEPADS];
|
||||
|
||||
static int gamePadBindingsModifiedFlags;
|
||||
static int gamePadBindingsModifiedFlags;
|
||||
|
||||
static Dvar::Var gpad_enabled;
|
||||
static Dvar::Var gpad_debug;
|
||||
static Dvar::Var gpad_present;
|
||||
static Dvar::Var gpad_in_use;
|
||||
static Dvar::Var gpad_style;
|
||||
static Dvar::Var gpad_sticksConfig;
|
||||
static Dvar::Var gpad_buttonConfig;
|
||||
static Dvar::Var gpad_menu_scroll_delay_first;
|
||||
static Dvar::Var gpad_menu_scroll_delay_rest;
|
||||
static Dvar::Var gpad_rumble;
|
||||
static Dvar::Var gpad_stick_pressed_hysteresis;
|
||||
static Dvar::Var gpad_stick_pressed;
|
||||
static Dvar::Var gpad_stick_deadzone_max;
|
||||
static Dvar::Var gpad_stick_deadzone_min;
|
||||
static Dvar::Var gpad_button_deadzone;
|
||||
static Dvar::Var gpad_button_rstick_deflect_max;
|
||||
static Dvar::Var gpad_button_lstick_deflect_max;
|
||||
static Dvar::Var gpad_use_hold_time;
|
||||
static Dvar::Var gpad_lockon_enabled;
|
||||
static Dvar::Var gpad_slowdown_enabled;
|
||||
static Dvar::Var input_viewSensitivity;
|
||||
static Dvar::Var input_invertPitch;
|
||||
static Dvar::Var sv_allowAimAssist;
|
||||
static Dvar::Var aim_turnrate_pitch;
|
||||
static Dvar::Var aim_turnrate_pitch_ads;
|
||||
static Dvar::Var aim_turnrate_yaw;
|
||||
static Dvar::Var aim_turnrate_yaw_ads;
|
||||
static Dvar::Var aim_accel_turnrate_enabled;
|
||||
static Dvar::Var aim_accel_turnrate_lerp;
|
||||
static Dvar::Var aim_input_graph_enabled;
|
||||
static Dvar::Var aim_input_graph_index;
|
||||
static Dvar::Var aim_scale_view_axis;
|
||||
static Dvar::Var cl_bypassMouseInput;
|
||||
static Dvar::Var cg_mapLocationSelectionCursorSpeed;
|
||||
static Dvar::Var aim_aimAssistRangeScale;
|
||||
static Dvar::Var aim_slowdown_enabled;
|
||||
static Dvar::Var aim_slowdown_debug;
|
||||
static Dvar::Var aim_slowdown_pitch_scale;
|
||||
static Dvar::Var aim_slowdown_pitch_scale_ads;
|
||||
static Dvar::Var aim_slowdown_yaw_scale;
|
||||
static Dvar::Var aim_slowdown_yaw_scale_ads;
|
||||
static Dvar::Var aim_lockon_enabled;
|
||||
static Dvar::Var aim_lockon_deflection;
|
||||
static Dvar::Var aim_lockon_pitch_strength;
|
||||
static Dvar::Var aim_lockon_strength;
|
||||
static Dvar::Var gpad_enabled;
|
||||
static Dvar::Var gpad_debug;
|
||||
static Dvar::Var gpad_present;
|
||||
static Dvar::Var gpad_in_use;
|
||||
static Dvar::Var gpad_style;
|
||||
static Dvar::Var gpad_sticksConfig;
|
||||
static Dvar::Var gpad_buttonConfig;
|
||||
static Dvar::Var gpad_menu_scroll_delay_first;
|
||||
static Dvar::Var gpad_menu_scroll_delay_rest;
|
||||
static Dvar::Var gpad_rumble;
|
||||
static Dvar::Var gpad_stick_pressed_hysteresis;
|
||||
static Dvar::Var gpad_stick_pressed;
|
||||
static Dvar::Var gpad_stick_deadzone_max;
|
||||
static Dvar::Var gpad_stick_deadzone_min;
|
||||
static Dvar::Var gpad_button_deadzone;
|
||||
static Dvar::Var gpad_button_rstick_deflect_max;
|
||||
static Dvar::Var gpad_button_lstick_deflect_max;
|
||||
static Dvar::Var gpad_use_hold_time;
|
||||
static Dvar::Var gpad_lockon_enabled;
|
||||
static Dvar::Var gpad_slowdown_enabled;
|
||||
static Dvar::Var input_viewSensitivity;
|
||||
static Dvar::Var input_invertPitch;
|
||||
static Dvar::Var sv_allowAimAssist;
|
||||
static Dvar::Var aim_turnrate_pitch;
|
||||
static Dvar::Var aim_turnrate_pitch_ads;
|
||||
static Dvar::Var aim_turnrate_yaw;
|
||||
static Dvar::Var aim_turnrate_yaw_ads;
|
||||
static Dvar::Var aim_accel_turnrate_enabled;
|
||||
static Dvar::Var aim_accel_turnrate_lerp;
|
||||
static Dvar::Var aim_input_graph_enabled;
|
||||
static Dvar::Var aim_input_graph_index;
|
||||
static Dvar::Var aim_scale_view_axis;
|
||||
static Dvar::Var cl_bypassMouseInput;
|
||||
static Dvar::Var cg_mapLocationSelectionCursorSpeed;
|
||||
static Dvar::Var aim_aimAssistRangeScale;
|
||||
static Dvar::Var aim_slowdown_enabled;
|
||||
static Dvar::Var aim_slowdown_debug;
|
||||
static Dvar::Var aim_slowdown_pitch_scale;
|
||||
static Dvar::Var aim_slowdown_pitch_scale_ads;
|
||||
static Dvar::Var aim_slowdown_yaw_scale;
|
||||
static Dvar::Var aim_slowdown_yaw_scale_ads;
|
||||
static Dvar::Var aim_lockon_enabled;
|
||||
static Dvar::Var aim_lockon_deflection;
|
||||
static Dvar::Var aim_lockon_pitch_strength;
|
||||
static Dvar::Var aim_lockon_strength;
|
||||
|
||||
static void MSG_WriteDeltaUsercmdKeyStub();
|
||||
static void MSG_WriteDeltaUsercmdKeyStub();
|
||||
|
||||
static void ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to);
|
||||
static void ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to);
|
||||
|
||||
static void MSG_ReadDeltaUsercmdKeyStub();
|
||||
static void MSG_ReadDeltaUsercmdKeyStub2();
|
||||
static void MSG_ReadDeltaUsercmdKeyStub();
|
||||
static void MSG_ReadDeltaUsercmdKeyStub2();
|
||||
|
||||
static float LinearTrack(float target, float current, float rate, float deltaTime);
|
||||
static bool AimAssist_DoBoundsIntersectCenterBox(const float* clipMins, const float* clipMaxs, float clipHalfWidth, float clipHalfHeight);
|
||||
static bool AimAssist_IsPlayerUsingOffhand(Game::AimAssistPlayerState* ps);
|
||||
static const Game::AimScreenTarget* AimAssist_GetBestTarget(const Game::AimAssistGlobals* aaGlob, float range, float regionWidth, float regionHeight);
|
||||
static const Game::AimScreenTarget* AimAssist_GetTargetFromEntity(const Game::AimAssistGlobals* aaGlob, int entIndex);
|
||||
static const Game::AimScreenTarget* AimAssist_GetPrevOrBestTarget(const Game::AimAssistGlobals* aaGlob, float range, float regionWidth, float regionHeight, int prevTargetEnt);
|
||||
static bool AimAssist_IsLockonActive(int gamePadIndex);
|
||||
static void AimAssist_ApplyLockOn(const Game::AimInput* input, Game::AimOutput* output);
|
||||
static void AimAssist_CalcAdjustedAxis(const Game::AimInput* input, float* pitchAxis, float* yawAxis);
|
||||
static bool AimAssist_IsSlowdownActive(const Game::AimAssistPlayerState* ps);
|
||||
static void AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale);
|
||||
static float AimAssist_Lerp(float from, float to, float fraction);
|
||||
static void AimAssist_ApplyTurnRates(const Game::AimInput* input, Game::AimOutput* output);
|
||||
static void AimAssist_UpdateGamePadInput(const Game::AimInput* input, Game::AimOutput* output);
|
||||
static float LinearTrack(float target, float current, float rate, float deltaTime);
|
||||
static bool AimAssist_DoBoundsIntersectCenterBox(const float* clipMins, const float* clipMaxs, float clipHalfWidth, float clipHalfHeight);
|
||||
static bool AimAssist_IsPlayerUsingOffhand(Game::AimAssistPlayerState* ps);
|
||||
static const Game::AimScreenTarget* AimAssist_GetBestTarget(const Game::AimAssistGlobals* aaGlob, float range, float regionWidth, float regionHeight);
|
||||
static const Game::AimScreenTarget* AimAssist_GetTargetFromEntity(const Game::AimAssistGlobals* aaGlob, int entIndex);
|
||||
static const Game::AimScreenTarget* AimAssist_GetPrevOrBestTarget(const Game::AimAssistGlobals* aaGlob, float range, float regionWidth, float regionHeight, int prevTargetEnt);
|
||||
static bool AimAssist_IsLockonActive(int gamePadIndex);
|
||||
static void AimAssist_ApplyLockOn(const Game::AimInput* input, Game::AimOutput* output);
|
||||
static void AimAssist_CalcAdjustedAxis(const Game::AimInput* input, float* pitchAxis, float* yawAxis);
|
||||
static bool AimAssist_IsSlowdownActive(const Game::AimAssistPlayerState* ps);
|
||||
static void AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale);
|
||||
static float AimAssist_Lerp(float from, float to, float fraction);
|
||||
static void AimAssist_ApplyTurnRates(const Game::AimInput* input, Game::AimOutput* output);
|
||||
static void AimAssist_UpdateGamePadInput(const Game::AimInput* input, Game::AimOutput* output);
|
||||
|
||||
static void CL_RemoteControlMove_GamePad(int localClientNum, Game::usercmd_s* cmd);
|
||||
static void CL_RemoteControlMove_Stub();
|
||||
static bool CG_HandleLocationSelectionInput_GamePad(int localClientNum, Game::usercmd_s* cmd);
|
||||
static void CG_HandleLocationSelectionInput_Stub();
|
||||
static bool CG_ShouldUpdateViewAngles(int localClientNum);
|
||||
static float CL_GamepadAxisValue(int gamePadIndex, Game::GamepadVirtualAxis virtualAxis);
|
||||
static char ClampChar(int value);
|
||||
static void CL_GamepadMove(int gamePadIndex, Game::usercmd_s* cmd, float frameTimeBase);
|
||||
static void CL_MouseMove_Stub();
|
||||
static void CL_RemoteControlMove_GamePad(int localClientNum, Game::usercmd_s* cmd);
|
||||
static void CL_RemoteControlMove_Stub();
|
||||
static bool CG_HandleLocationSelectionInput_GamePad(int localClientNum, Game::usercmd_s* cmd);
|
||||
static void CG_HandleLocationSelectionInput_Stub();
|
||||
static bool CG_ShouldUpdateViewAngles(int localClientNum);
|
||||
static float CL_GamepadAxisValue(int gamePadIndex, Game::GamepadVirtualAxis virtualAxis);
|
||||
static char ClampChar(int value);
|
||||
static void CL_GamepadMove(int gamePadIndex, Game::usercmd_s* cmd, float frameTimeBase);
|
||||
static void CL_MouseMove_Stub();
|
||||
|
||||
static bool Gamepad_ShouldUse(const Game::gentity_s* playerEnt, unsigned useTime);
|
||||
static void Player_UseEntity_Stub();
|
||||
static bool Gamepad_ShouldUse(const Game::gentity_s* playerEnt, unsigned useTime);
|
||||
static void Player_UseEntity_Stub();
|
||||
|
||||
static bool Key_IsValidGamePadChar(int key);
|
||||
static void CL_GamepadResetMenuScrollTime(int gamePadIndex, int key, bool down, unsigned int time);
|
||||
static bool Scoreboard_HandleInput(int gamePadIndex, int key);
|
||||
static bool CL_CheckForIgnoreDueToRepeat(int gamePadIndex, int key, int repeatCount, unsigned int time);
|
||||
static void UI_GamepadKeyEvent(int gamePadIndex, int key, bool down);
|
||||
static void CL_GamepadGenerateAPad(int gamePadIndex, Game::GamepadPhysicalAxis physicalAxis, unsigned time);
|
||||
static void CL_GamepadEvent(int gamePadIndex, Game::GamepadPhysicalAxis physicalAxis, float value, unsigned time);
|
||||
static void CL_GamepadButtonEvent(int gamePadIndex, int key, Game::GamePadButtonEvent buttonEvent, unsigned time);
|
||||
static void CL_GamepadButtonEventForPort(int gamePadIndex, int key, Game::GamePadButtonEvent buttonEvent, unsigned int time);
|
||||
static bool Key_IsValidGamePadChar(int key);
|
||||
static void CL_GamepadResetMenuScrollTime(int gamePadIndex, int key, bool down, unsigned int time);
|
||||
static bool Scoreboard_HandleInput(int gamePadIndex, int key);
|
||||
static bool CL_CheckForIgnoreDueToRepeat(int gamePadIndex, int key, int repeatCount, unsigned int time);
|
||||
static void UI_GamepadKeyEvent(int gamePadIndex, int key, bool down);
|
||||
static void CL_GamepadGenerateAPad(int gamePadIndex, Game::GamepadPhysicalAxis physicalAxis, unsigned time);
|
||||
static void CL_GamepadEvent(int gamePadIndex, Game::GamepadPhysicalAxis physicalAxis, float value, unsigned time);
|
||||
static void CL_GamepadButtonEvent(int gamePadIndex, int key, Game::GamePadButtonEvent buttonEvent, unsigned time);
|
||||
static void CL_GamepadButtonEventForPort(int gamePadIndex, int key, Game::GamePadButtonEvent buttonEvent, unsigned int time);
|
||||
|
||||
static void GPad_ConvertStickToFloat(short x, short y, float& outX, float& outY);
|
||||
static float GPad_GetStick(int gamePadIndex, Game::GamePadStick stick);
|
||||
static float GPad_GetButton(int gamePadIndex, Game::GamePadButton button);
|
||||
static bool GPad_IsButtonPressed(int gamePadIndex, Game::GamePadButton button);
|
||||
static bool GPad_ButtonRequiresUpdates(int gamePadIndex, Game::GamePadButton button);
|
||||
static bool GPad_IsButtonReleased(int gamePadIndex, Game::GamePadButton button);
|
||||
static void GPad_ConvertStickToFloat(short x, short y, float& outX, float& outY);
|
||||
static float GPad_GetStick(int gamePadIndex, Game::GamePadStick stick);
|
||||
static float GPad_GetButton(int gamePadIndex, Game::GamePadButton button);
|
||||
static bool GPad_IsButtonPressed(int gamePadIndex, Game::GamePadButton button);
|
||||
static bool GPad_ButtonRequiresUpdates(int gamePadIndex, Game::GamePadButton button);
|
||||
static bool GPad_IsButtonReleased(int gamePadIndex, Game::GamePadButton button);
|
||||
|
||||
static void GPad_UpdateSticksDown(int gamePadIndex);
|
||||
static void GPad_UpdateSticks(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||
static void GPad_UpdateDigitals(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||
static void GPad_UpdateAnalogs(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||
static void GPad_UpdateSticksDown(int gamePadIndex);
|
||||
static void GPad_UpdateSticks(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||
static void GPad_UpdateDigitals(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||
static void GPad_UpdateAnalogs(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||
|
||||
static bool GPad_Check(int gamePadIndex, int portIndex);
|
||||
static void GPad_RefreshAll();
|
||||
static void GPad_UpdateAll();
|
||||
static void IN_GamePadsMove();
|
||||
static void IN_Frame_Hk();
|
||||
static bool GPad_Check(int gamePadIndex, int portIndex);
|
||||
static void GPad_RefreshAll();
|
||||
static void GPad_UpdateAll();
|
||||
static void IN_GamePadsMove();
|
||||
static void IN_Frame_Hk();
|
||||
|
||||
static void Gamepad_WriteBindings(int gamePadIndex, int handle);
|
||||
static void Key_WriteBindings_Hk(int localClientNum, int handle);
|
||||
static void Com_WriteConfiguration_Modified_Stub();
|
||||
static void Gamepad_WriteBindings(int gamePadIndex, int handle);
|
||||
static void Key_WriteBindings_Hk(int localClientNum, int handle);
|
||||
static void Com_WriteConfiguration_Modified_Stub();
|
||||
|
||||
static void Gamepad_BindAxis(int gamePadIndex, Game::GamepadPhysicalAxis realIndex, Game::GamepadVirtualAxis axisIndex, Game::GamepadMapping mapType);
|
||||
static Game::GamepadPhysicalAxis StringToPhysicalAxis(const char* str);
|
||||
static Game::GamepadVirtualAxis StringToVirtualAxis(const char* str);
|
||||
static Game::GamepadMapping StringToGamePadMapping(const char* str);
|
||||
static void Axis_Bind_f(Command::Params* params);
|
||||
static void Axis_Unbindall_f(Command::Params* params);
|
||||
static void Bind_GP_SticksConfigs_f(Command::Params* params);
|
||||
static void Bind_GP_ButtonsConfigs_f(Command::Params* params);
|
||||
static void Scores_Toggle_f(Command::Params* params);
|
||||
static void Gamepad_BindAxis(int gamePadIndex, Game::GamepadPhysicalAxis realIndex, Game::GamepadVirtualAxis axisIndex, Game::GamepadMapping mapType);
|
||||
static Game::GamepadPhysicalAxis StringToPhysicalAxis(const char* str);
|
||||
static Game::GamepadVirtualAxis StringToVirtualAxis(const char* str);
|
||||
static Game::GamepadMapping StringToGamePadMapping(const char* str);
|
||||
static void Axis_Bind_f(Command::Params* params);
|
||||
static void Axis_Unbindall_f(Command::Params* params);
|
||||
static void Bind_GP_SticksConfigs_f(Command::Params* params);
|
||||
static void Bind_GP_ButtonsConfigs_f(Command::Params* params);
|
||||
static void Scores_Toggle_f(Command::Params* params);
|
||||
|
||||
static void InitDvars();
|
||||
static void CG_RegisterDvars_Hk();
|
||||
static void InitDvars();
|
||||
static void CG_RegisterDvars_Hk();
|
||||
|
||||
static const char* GetGamePadCommand(const char* command);
|
||||
static int Key_GetCommandAssignmentInternal_Hk(const char* cmd, int(*keys)[2]);
|
||||
static bool IsGamePadInUse();
|
||||
static void CL_KeyEvent_Hk(int localClientNum, int key, int down, unsigned int time);
|
||||
static int CL_MouseEvent_Hk(int x, int y, int dx, int dy);
|
||||
static bool UI_RefreshViewport_Hk();
|
||||
static const char* GetGamePadCommand(const char* command);
|
||||
static int Key_GetCommandAssignmentInternal_Hk(const char* cmd, int(*keys)[2]);
|
||||
static bool IsGamePadInUse();
|
||||
static void CL_KeyEvent_Hk(int localClientNum, int key, int down, unsigned int time);
|
||||
static int CL_MouseEvent_Hk(int x, int y, int dx, int dy);
|
||||
static bool UI_RefreshViewport_Hk();
|
||||
|
||||
static Game::keyname_t* GetLocalizedKeyNameMap();
|
||||
static void GetLocalizedKeyName_Stub();
|
||||
static void CreateKeyNameMap();
|
||||
};
|
||||
static Game::keyname_t* GetLocalizedKeyNameMap();
|
||||
static void GetLocalizedKeyName_Stub();
|
||||
static void CreateKeyNameMap();
|
||||
};
|
||||
}
|
||||
|
@ -98,15 +98,5 @@ namespace Components
|
||||
// Dynamically grab gametypes
|
||||
Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList
|
||||
Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList
|
||||
|
||||
// This is placed here in case the anticheat has been disabled!
|
||||
// Make sure this is called after every onther anticheat check!
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Utils::Hook(0x5ACBA3, []() // Somewhere in the renderer, past other renderer hooks!
|
||||
{
|
||||
AntiCheat::FlagIntegrityCheck();
|
||||
return Utils::Hook::Call<void()>(0x50AB20)();
|
||||
}, HOOK_CALL).install()->quick();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -297,13 +297,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// #ifndef DISABLE_ANTICHEAT
|
||||
// if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && !Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests())
|
||||
// {
|
||||
// AntiCheat::PatchVirtualProtect(VirtualProtect, VirtualProtectEx);
|
||||
// }
|
||||
// #endif
|
||||
}
|
||||
|
||||
Localization::~Localization()
|
||||
|
@ -77,16 +77,14 @@ namespace Components
|
||||
|
||||
std::string Logger::Format(const char** message)
|
||||
{
|
||||
const size_t bufferSize = 0x10000;
|
||||
Utils::Memory::Allocator allocator;
|
||||
char* buffer = allocator.allocateArray<char>(bufferSize);
|
||||
char buffer[4096] = {0};
|
||||
|
||||
va_list ap = reinterpret_cast<char*>(const_cast<char**>(&message[1]));
|
||||
//va_start(ap, *message);
|
||||
_vsnprintf_s(buffer, bufferSize, bufferSize, *message, ap);
|
||||
|
||||
_vsnprintf_s(buffer, _TRUNCATE, *message, ap);
|
||||
va_end(ap);
|
||||
|
||||
return buffer;
|
||||
return {buffer};
|
||||
}
|
||||
|
||||
void Logger::Flush()
|
||||
@ -138,8 +136,8 @@ namespace Components
|
||||
{
|
||||
if (!data) return;
|
||||
|
||||
std::string buffer(data);
|
||||
for (auto& addr : Logger::LoggingAddresses[gLog & 1])
|
||||
const std::string buffer(data);
|
||||
for (const auto& addr : Logger::LoggingAddresses[gLog & 1])
|
||||
{
|
||||
Network::SendCommand(addr, "print", buffer);
|
||||
}
|
||||
@ -373,9 +371,9 @@ namespace Components
|
||||
Logger::MessageMutex.unlock();
|
||||
|
||||
// Flush the console log
|
||||
if (int fh = *reinterpret_cast<int*>(0x1AD8F28))
|
||||
if (const auto logfile = *reinterpret_cast<int*>(0x1AD8F28))
|
||||
{
|
||||
Game::FS_FCloseFile(fh);
|
||||
Game::FS_FCloseFile(logfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -788,7 +788,7 @@ namespace Components
|
||||
{
|
||||
int dlc = token.get<int>();
|
||||
|
||||
for (auto pack : Maps::DlcPacks)
|
||||
for (const auto& pack : Maps::DlcPacks)
|
||||
{
|
||||
if (pack.index == dlc)
|
||||
{
|
||||
|
@ -70,7 +70,7 @@ namespace Components
|
||||
|
||||
script->next = nullptr;
|
||||
|
||||
Game::source_t* source = allocator->allocate<Game::source_t>();
|
||||
auto* source = allocator->allocate<Game::source_t>();
|
||||
if (!source)
|
||||
{
|
||||
Game::FreeMemory(script);
|
||||
@ -116,7 +116,7 @@ namespace Components
|
||||
{
|
||||
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||
|
||||
Game::menuDef_t* menu = allocator->allocate<Game::menuDef_t>();
|
||||
auto* menu = allocator->allocate<Game::menuDef_t>();
|
||||
if (!menu) return nullptr;
|
||||
|
||||
menu->items = allocator->allocateArray<Game::itemDef_s*>(512);
|
||||
@ -284,17 +284,7 @@ namespace Components
|
||||
|
||||
if (menus.empty())
|
||||
{
|
||||
// // Try loading the original menu, if we can't load our custom one
|
||||
// Game::menuDef_t* originalMenu = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, menudef->window.name).menu;
|
||||
//
|
||||
// if (originalMenu)
|
||||
// {
|
||||
// menus.push_back({ false, originalMenu });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
menus.push_back({ false, menudef }); // Native menu
|
||||
// }
|
||||
}
|
||||
|
||||
return menus;
|
||||
@ -308,7 +298,7 @@ namespace Components
|
||||
if (menus.empty()) return nullptr;
|
||||
|
||||
// Allocate new menu list
|
||||
Game::MenuList* newList = allocator->allocate<Game::MenuList>();
|
||||
auto* newList = allocator->allocate<Game::MenuList>();
|
||||
if (!newList) return nullptr;
|
||||
|
||||
newList->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size());
|
||||
@ -319,7 +309,7 @@ namespace Components
|
||||
}
|
||||
|
||||
newList->name = allocator->duplicateString(menu);
|
||||
newList->menuCount = menus.size();
|
||||
newList->menuCount = static_cast<int>(menus.size());
|
||||
|
||||
// Copy new menus
|
||||
for (unsigned int i = 0; i < menus.size(); ++i)
|
||||
@ -759,50 +749,14 @@ namespace Components
|
||||
|
||||
void Menus::RegisterCustomMenusHook()
|
||||
{
|
||||
Game::UiContext* uiInfoArray = (Game::UiContext*)0x62E2858;
|
||||
// Game::MenuList list;
|
||||
|
||||
Utils::Hook::Call<void()>(0x401700)(); // call original load functions
|
||||
//Game::XAssetHeader header = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENULIST, "ui_mp/iw4x.txt");
|
||||
//if (header.data && !(header.menuList->menuCount == 1 && !_stricmp("default_menu", header.menuList->menus[0]->window.name)))
|
||||
//{
|
||||
// // Utils::Hook::Call<void(void*, Game::MenuList*, int)>(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus
|
||||
// std::memcpy(&list, header.data, sizeof(Game::MenuList));
|
||||
|
||||
// for (int i = 0; i < uiInfoArray->menuCount; i++)
|
||||
// {
|
||||
// for (int j = 0; j < list.menuCount; j++)
|
||||
// {
|
||||
// if (!list.menus[j]) continue; // skip already used entries
|
||||
// if (!stricmp(list.menus[j]->window.name, uiInfoArray->Menus[i]->window.name))
|
||||
// {
|
||||
// uiInfoArray->Menus[i] = list.menus[j]; // overwrite UiContext pointer
|
||||
// list.menus[j] = nullptr; // clear entries that already exist so we don't add them later
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// for (int i = 0; i < list.menuCount; i++)
|
||||
// {
|
||||
// if (list.menus[i])
|
||||
// {
|
||||
// uiInfoArray->Menus[uiInfoArray->menuCount++] = list.menus[i];
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
for (int i = 0; i < uiInfoArray->menuCount; i++)
|
||||
#ifdef _DEBUG
|
||||
for (int i = 0; i < Game::uiContext->menuCount; i++)
|
||||
{
|
||||
OutputDebugStringA(Utils::String::VA("%s\n", uiInfoArray->Menus[i]->window.name));
|
||||
OutputDebugStringA(Utils::String::VA("%s\n", Game::uiContext->Menus[i]->window.name));
|
||||
}
|
||||
|
||||
/*
|
||||
header = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENULIST, "ui_mp/mod.txt");
|
||||
if (header.data && !(header.menuList->menuCount == 1 && !_stricmp("default_menu", header.menuList->menus[0]->window.name)))
|
||||
{
|
||||
Utils::Hook::Call<void(void*, Game::MenuList*, int)>(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
Menus::Menus()
|
||||
@ -824,12 +778,12 @@ namespace Components
|
||||
|
||||
// Use the connect menu open call to update server motds
|
||||
Utils::Hook(0x428E48, []()
|
||||
{
|
||||
if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost)
|
||||
{
|
||||
if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost)
|
||||
{
|
||||
Dvar::Var("didyouknow").set(Party::GetMotd());
|
||||
}
|
||||
}, HOOK_CALL).install()->quick();
|
||||
Dvar::Var("didyouknow").set(Party::GetMotd());
|
||||
}
|
||||
}, HOOK_CALL).install()->quick();
|
||||
|
||||
// Intercept menu painting
|
||||
Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick();
|
||||
@ -844,53 +798,49 @@ namespace Components
|
||||
Utils::Hook::SetString(0x6FC790, "main_text");
|
||||
|
||||
Command::Add("openmenu", [](Command::Params* params)
|
||||
{
|
||||
if (params->size() != 2)
|
||||
{
|
||||
if (params->size() != 2)
|
||||
{
|
||||
Logger::Print("USAGE: openmenu <menu name>\n");
|
||||
return;
|
||||
}
|
||||
Logger::Print("USAGE: openmenu <menu name>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
|
||||
if (Dvar::Var("cl_ingame").get<bool>())
|
||||
{
|
||||
Game::Key_SetCatcher(0, 16);
|
||||
}
|
||||
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
|
||||
if (Dvar::Var("cl_ingame").get<bool>())
|
||||
{
|
||||
Game::Key_SetCatcher(0, 16);
|
||||
}
|
||||
|
||||
Game::Menus_OpenByName(Game::uiContext, params->get(1));
|
||||
});
|
||||
Game::Menus_OpenByName(Game::uiContext, params->get(1));
|
||||
});
|
||||
|
||||
Command::Add("reloadmenus", [](Command::Params*)
|
||||
{
|
||||
// Close all menus
|
||||
Game::Menus_CloseAll(Game::uiContext);
|
||||
|
||||
// Free custom menus
|
||||
Menus::FreeEverything();
|
||||
|
||||
// Only disconnect if in-game, context is updated automatically!
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
{
|
||||
// Close all menus
|
||||
Game::Menus_CloseAll(Game::uiContext);
|
||||
Game::Cbuf_AddText(0, "disconnect\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reinitialize ui context
|
||||
Utils::Hook::Call<void()>(0x401700)();
|
||||
|
||||
// Free custom menus
|
||||
Menus::FreeEverything();
|
||||
|
||||
// Only disconnect if in-game, context is updated automatically!
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
{
|
||||
Game::Cbuf_AddText(0, "disconnect\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reinitialize ui context
|
||||
Utils::Hook::Call<void()>(0x401700)();
|
||||
|
||||
// Reopen main menu
|
||||
Game::Menus_OpenByName(Game::uiContext, "main_text");
|
||||
}
|
||||
});
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner2);
|
||||
#endif
|
||||
// Reopen main menu
|
||||
Game::Menus_OpenByName(Game::uiContext, "main_text");
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("mp_QuickMessage", [](Command::Params*)
|
||||
{
|
||||
Command::Execute("openmenu quickmessage");
|
||||
});
|
||||
{
|
||||
Command::Execute("openmenu quickmessage");
|
||||
});
|
||||
|
||||
// Define custom menus here
|
||||
Menus::Add("ui_mp/changelog.menu");
|
||||
|
@ -2,355 +2,289 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Dvar::Var Movement::PlayerDuckedSpeedScale;
|
||||
Dvar::Var Movement::PlayerLastStandCrawlSpeedScale;
|
||||
Dvar::Var Movement::PlayerProneSpeedScale;
|
||||
Dvar::Var Movement::PlayerSpectateSpeedScale;
|
||||
Dvar::Var Movement::CGUfoScaler;
|
||||
Dvar::Var Movement::CGNoclipScaler;
|
||||
Dvar::Var Movement::BGBouncesAllAngles;
|
||||
Dvar::Var Movement::BGRocketJump;
|
||||
Dvar::Var Movement::BGPlayerEjection;
|
||||
Dvar::Var Movement::BGPlayerCollision;
|
||||
Game::dvar_t* Movement::BGBounces;
|
||||
|
||||
float Movement::PM_CmdScaleForStance(const Game::pmove_s* pm)
|
||||
{
|
||||
assert(pm->ps != nullptr);
|
||||
|
||||
const auto* playerState = pm->ps;
|
||||
float scale;
|
||||
|
||||
if (playerState->viewHeightLerpTime != 0 && playerState->viewHeightLerpTarget == 0xB)
|
||||
{
|
||||
scale = pm->cmd.serverTime - playerState->viewHeightLerpTime / 400.0f;
|
||||
|
||||
if (0.0f <= scale)
|
||||
{
|
||||
if (scale > 1.0f)
|
||||
{
|
||||
scale = 1.0f;
|
||||
return scale * 0.15f + (1.0f - scale) * 0.65f;
|
||||
}
|
||||
|
||||
if (scale != 0.0f)
|
||||
{
|
||||
return scale * 0.15f + (1.0f - scale) * 0.65f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((playerState->viewHeightLerpTime != 0 && playerState->viewHeightLerpTarget == 0x28) &&
|
||||
playerState->viewHeightLerpDown == 0)
|
||||
{
|
||||
scale = 400.0f / pm->cmd.serverTime - playerState->viewHeightLerpTime;
|
||||
|
||||
if (0.0f <= scale)
|
||||
{
|
||||
if (scale > 1.0f)
|
||||
{
|
||||
scale = 1.0f;
|
||||
}
|
||||
else if (scale != 0.0f)
|
||||
{
|
||||
return scale * 0.65f + (1.0f - scale) * 0.15f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scale = 1.0f;
|
||||
const auto stance = Game::PM_GetEffectiveStance(playerState);
|
||||
|
||||
if (stance == Game::PM_EFF_STANCE_PRONE)
|
||||
{
|
||||
scale = Movement::PlayerProneSpeedScale.get<float>();
|
||||
}
|
||||
|
||||
else if (stance == Game::PM_EFF_STANCE_DUCKED)
|
||||
{
|
||||
scale = Movement::PlayerDuckedSpeedScale.get<float>();
|
||||
}
|
||||
|
||||
else if (stance == Game::PM_EFF_STANCE_LASTSTANDCRAWL)
|
||||
{
|
||||
scale = Movement::PlayerLastStandCrawlSpeedScale.get<float>();
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
__declspec(naked) void Movement::PM_CmdScaleForStanceStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push edx
|
||||
call Movement::PM_CmdScaleForStance // pm
|
||||
add esp, 4
|
||||
|
||||
popad
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
float Movement::PM_MoveScale(Game::playerState_s* ps, float forwardmove,
|
||||
float rightmove, float upmove)
|
||||
{
|
||||
assert(ps != nullptr);
|
||||
|
||||
auto max = (std::fabsf(forwardmove) < std::fabsf(rightmove))
|
||||
? std::fabsf(rightmove)
|
||||
: std::fabsf(forwardmove);
|
||||
|
||||
if (std::fabsf(upmove) > max)
|
||||
{
|
||||
max = std::fabsf(upmove);
|
||||
}
|
||||
|
||||
if (max == 0.0f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
auto total = std::sqrtf(forwardmove * forwardmove
|
||||
+ rightmove * rightmove + upmove * upmove);
|
||||
auto scale = (ps->speed * max) / (127.0f * total);
|
||||
|
||||
if (ps->pm_flags & Game::PMF_WALKING || ps->leanf != 0.0f)
|
||||
{
|
||||
scale *= 0.4f;
|
||||
}
|
||||
|
||||
if (ps->pm_type == Game::PM_NOCLIP)
|
||||
{
|
||||
return scale * Movement::CGNoclipScaler.get<float>();
|
||||
}
|
||||
|
||||
if (ps->pm_type == Game::PM_UFO)
|
||||
{
|
||||
return scale * Movement::CGUfoScaler.get<float>();
|
||||
}
|
||||
|
||||
if (ps->pm_type == Game::PM_SPECTATOR)
|
||||
{
|
||||
return scale * Movement::PlayerSpectateSpeedScale.get<float>();
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
__declspec(naked) void Movement::PM_MoveScaleStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push [esp + 0xC + 0x20] // upmove
|
||||
push [esp + 0xC + 0x20] // rightmove
|
||||
push [esp + 0xC + 0x20] // forwardmove
|
||||
push esi // ps
|
||||
call Movement::PM_MoveScale
|
||||
add esp, 0x10
|
||||
|
||||
popad
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Movement::PM_StepSlideMoveStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// Check the value of BGBounces
|
||||
push ecx
|
||||
push eax
|
||||
|
||||
mov eax, Movement::BGBounces
|
||||
mov ecx, dword ptr [eax + 0x10]
|
||||
test ecx, ecx
|
||||
|
||||
pop eax
|
||||
pop ecx
|
||||
|
||||
// Do not bounce if BGBounces is 0
|
||||
jle noBounce
|
||||
|
||||
// Bounce
|
||||
push 0x4B1B34
|
||||
retn
|
||||
|
||||
noBounce:
|
||||
// Original game code
|
||||
cmp dword ptr [esp + 0x24], 0
|
||||
push 0x4B1B48
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void Movement::PM_ProjectVelocityStub(const float* velIn, const float* normal, float* velOut)
|
||||
{
|
||||
const auto lengthSquared2D = velIn[0] * velIn[0] + velIn[1] * velIn[1];
|
||||
|
||||
if (std::fabsf(normal[2]) < 0.001f || lengthSquared2D == 0.0)
|
||||
{
|
||||
velOut[0] = velIn[0];
|
||||
velOut[1] = velIn[1];
|
||||
velOut[2] = velIn[2];
|
||||
return;
|
||||
}
|
||||
|
||||
auto newZ = velIn[0] * normal[0] + velIn[1] * normal[1];
|
||||
newZ = -newZ / normal[2];
|
||||
const auto lengthScale = std::sqrtf((velIn[2] * velIn[2] + lengthSquared2D)
|
||||
/ (newZ * newZ + lengthSquared2D));
|
||||
|
||||
if (Movement::BGBouncesAllAngles.get<bool>()
|
||||
|| (lengthScale < 1.f || newZ < 0.f || velIn[2] > 0.f))
|
||||
{
|
||||
velOut[0] = velIn[0] * lengthScale;
|
||||
velOut[1] = velIn[1] * lengthScale;
|
||||
velOut[2] = newZ * lengthScale;
|
||||
}
|
||||
}
|
||||
|
||||
// Double bounces
|
||||
void Movement::Jump_ClearState_Hk(Game::playerState_s* ps)
|
||||
{
|
||||
if (Movement::BGBounces->current.integer != Movement::DOUBLE)
|
||||
{
|
||||
Game::Jump_ClearState(ps);
|
||||
}
|
||||
}
|
||||
|
||||
Game::gentity_s* Movement::Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex,
|
||||
float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool a7)
|
||||
{
|
||||
auto* result = Game::Weapon_RocketLauncher_Fire(ent, weaponIndex, spread, wp, gunVel, lockParms, a7);
|
||||
|
||||
if (ent->client != nullptr && BGRocketJump.get<bool>())
|
||||
{
|
||||
ent->client->ps.velocity[0] += (0 - wp->forward[0]) * 64.0f;
|
||||
ent->client->ps.velocity[1] += (0 - wp->forward[1]) * 64.0f;
|
||||
ent->client->ps.velocity[2] += (0 - wp->forward[2]) * 64.0f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Movement::StuckInClient_Hk(Game::gentity_s* self)
|
||||
{
|
||||
if (Movement::BGPlayerEjection.get<bool>())
|
||||
{
|
||||
return Utils::Hook::Call<int(Game::gentity_s*)>(0x402D30)(self); // StuckInClient
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Movement::CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end,
|
||||
const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles)
|
||||
{
|
||||
if (Movement::BGPlayerCollision.get<bool>())
|
||||
{
|
||||
Utils::Hook::Call<void(Game::trace_t*, const float*, const float*,
|
||||
const Game::Bounds*, const Game::Bounds*, int, const float*, const float*)>
|
||||
(0x478300)
|
||||
(results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* Movement::Dvar_RegisterLastStandSpeedScale(const char* dvarName, float value,
|
||||
float min, float max, unsigned __int16 /*flags*/, const char* description)
|
||||
{
|
||||
Movement::PlayerLastStandCrawlSpeedScale = Dvar::Register<float>(dvarName, value,
|
||||
min, max, Game::DVAR_CHEAT | Game::DVAR_CODINFO, description);
|
||||
|
||||
return Movement::PlayerLastStandCrawlSpeedScale.get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
Game::dvar_t* Movement::Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value,
|
||||
float min, float max, unsigned __int16 /*flags*/, const char* description)
|
||||
{
|
||||
Movement::PlayerSpectateSpeedScale = Dvar::Register<float>(dvarName, value,
|
||||
min, max, Game::DVAR_CHEAT | Game::DVAR_CODINFO, description);
|
||||
|
||||
return Movement::PlayerSpectateSpeedScale.get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
Movement::Movement()
|
||||
{
|
||||
Dvar::OnInit([]
|
||||
{
|
||||
static const char* bg_bouncesValues[] =
|
||||
{
|
||||
"disabled",
|
||||
"enabled",
|
||||
"double",
|
||||
nullptr
|
||||
};
|
||||
|
||||
Movement::PlayerDuckedSpeedScale = Dvar::Register<float>("player_duckedSpeedScale",
|
||||
0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The scale applied to the player speed when ducking");
|
||||
|
||||
Movement::PlayerProneSpeedScale = Dvar::Register<float>("player_proneSpeedScale",
|
||||
0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The scale applied to the player speed when crawling");
|
||||
|
||||
// 3arc naming convention
|
||||
Movement::CGUfoScaler = Dvar::Register<float>("cg_ufo_scaler",
|
||||
6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The speed at which ufo camera moves");
|
||||
|
||||
Movement::CGNoclipScaler = Dvar::Register<float>("cg_noclip_scaler",
|
||||
3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The speed at which noclip camera moves");
|
||||
|
||||
Movement::BGBounces = Game::Dvar_RegisterEnum("bg_bounces",
|
||||
bg_bouncesValues, Movement::DISABLED, Game::DVAR_CODINFO, "Bounce glitch settings");
|
||||
|
||||
Movement::BGBouncesAllAngles = Dvar::Register<bool>("bg_bouncesAllAngles",
|
||||
false, Game::DVAR_CODINFO, "Force bounce from all angles");
|
||||
|
||||
Movement::BGRocketJump = Dvar::Register<bool>("bg_rocketJump",
|
||||
false, Game::DVAR_CODINFO, "Enable CoD4 rocket jumps");
|
||||
|
||||
Movement::BGPlayerEjection = Dvar::Register<bool>("bg_playerEjection",
|
||||
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
||||
|
||||
Movement::BGPlayerCollision = Dvar::Register<bool>("bg_playerCollision",
|
||||
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
||||
});
|
||||
|
||||
// Hook PM_CmdScaleForStance in PM_CmdScale_Walk
|
||||
Utils::Hook(0x572F34, Movement::PM_CmdScaleForStanceStub, HOOK_CALL).install()->quick();
|
||||
|
||||
//Hook PM_CmdScaleForStance in PM_GetMaxSpeed
|
||||
Utils::Hook(0x57395F, Movement::PM_CmdScaleForStanceStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used.
|
||||
Utils::Hook(0x448B66, Movement::Dvar_RegisterLastStandSpeedScale, HOOK_CALL).install()->quick();
|
||||
|
||||
// Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used.
|
||||
Utils::Hook(0x448990, Movement::Dvar_RegisterSpectateSpeedScale, HOOK_CALL).install()->quick();
|
||||
|
||||
// Hook PM_MoveScale so we can add custom speed scale for Ufo and Noclip
|
||||
Utils::Hook(0x56F845, Movement::PM_MoveScaleStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x56FABD, Movement::PM_MoveScaleStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Bounce logic
|
||||
Utils::Hook(0x4B1B2D, Movement::PM_StepSlideMoveStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x57383E, Movement::Jump_ClearState_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4B1B97, Movement::PM_ProjectVelocityStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Rocket jump
|
||||
Utils::Hook(0x4A4F9B, Movement::Weapon_RocketLauncher_Fire_Hk, HOOK_CALL).install()->quick(); // FireWeapon
|
||||
|
||||
// Hook StuckInClient & CM_TransformedCapsuleTrace
|
||||
// so we can prevent intersecting players from being pushed away from each other
|
||||
Utils::Hook(0x5D8153, Movement::StuckInClient_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x45A5BF, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity
|
||||
Utils::Hook(0x5A0CAD, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity
|
||||
}
|
||||
Dvar::Var Movement::PlayerSpectateSpeedScale;
|
||||
Dvar::Var Movement::CGUfoScaler;
|
||||
Dvar::Var Movement::CGNoclipScaler;
|
||||
Dvar::Var Movement::BGBouncesAllAngles;
|
||||
Dvar::Var Movement::BGRocketJump;
|
||||
Dvar::Var Movement::BGPlayerEjection;
|
||||
Dvar::Var Movement::BGPlayerCollision;
|
||||
Game::dvar_t* Movement::BGBounces;
|
||||
Game::dvar_t* Movement::PlayerDuckedSpeedScale;
|
||||
Game::dvar_t* Movement::PlayerProneSpeedScale;
|
||||
|
||||
__declspec(naked) void Movement::PM_PlayerDuckedSpeedScaleStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
mov eax, Movement::PlayerDuckedSpeedScale
|
||||
fld dword ptr [eax + 0x10] // dvar_t.current.value
|
||||
pop eax
|
||||
|
||||
// Game's code
|
||||
pop ecx
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Movement::PM_PlayerProneSpeedScaleStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
mov eax, Movement::PlayerProneSpeedScale
|
||||
fld dword ptr [eax + 0x10] // dvar_t.current.value
|
||||
pop eax
|
||||
|
||||
// Game's code
|
||||
pop ecx
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
float Movement::PM_MoveScale(Game::playerState_s* ps, float fmove,
|
||||
float rmove, float umove)
|
||||
{
|
||||
assert(ps != nullptr);
|
||||
|
||||
auto max = std::fabsf(fmove) < std::fabsf(rmove)
|
||||
? std::fabsf(rmove) : std::fabsf(fmove);
|
||||
|
||||
if (std::fabsf(umove) > max)
|
||||
{
|
||||
max = std::fabsf(umove);
|
||||
}
|
||||
|
||||
if (max == 0.0f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
auto total = std::sqrtf(fmove * fmove
|
||||
+ rmove * rmove + umove * umove);
|
||||
auto scale = (static_cast<float>(ps->speed) * max) / (127.0f * total);
|
||||
|
||||
if (ps->pm_flags & Game::PMF_WALKING || ps->leanf != 0.0f)
|
||||
{
|
||||
scale *= 0.4f;
|
||||
}
|
||||
|
||||
switch (ps->pm_type)
|
||||
{
|
||||
case Game::pmtype_t::PM_NOCLIP:
|
||||
scale *= Movement::CGNoclipScaler.get<float>();
|
||||
break;
|
||||
case Game::pmtype_t::PM_UFO:
|
||||
scale *= Movement::CGUfoScaler.get<float>();
|
||||
break;
|
||||
case Game::pmtype_t::PM_SPECTATOR:
|
||||
scale *= Movement::PlayerSpectateSpeedScale.get<float>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
__declspec(naked) void Movement::PM_MoveScaleStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push [esp + 0xC + 0x20] // umove
|
||||
push [esp + 0xC + 0x20] // rmove
|
||||
push [esp + 0xC + 0x20] // fmove
|
||||
push esi // ps
|
||||
call Movement::PM_MoveScale
|
||||
add esp, 0x10
|
||||
|
||||
popad
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Movement::PM_StepSlideMoveStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// Check the value of BGBounces
|
||||
push ecx
|
||||
push eax
|
||||
|
||||
mov eax, Movement::BGBounces
|
||||
mov ecx, dword ptr [eax + 0x10]
|
||||
test ecx, ecx
|
||||
|
||||
pop eax
|
||||
pop ecx
|
||||
|
||||
// Do not bounce if BGBounces is 0
|
||||
jle noBounce
|
||||
|
||||
// Bounce
|
||||
push 0x4B1B34
|
||||
retn
|
||||
|
||||
noBounce:
|
||||
// Original game code
|
||||
cmp dword ptr [esp + 0x24], 0
|
||||
push 0x4B1B48
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void Movement::PM_ProjectVelocityStub(const float* velIn, const float* normal, float* velOut)
|
||||
{
|
||||
const auto lengthSquared2D = velIn[0] * velIn[0] + velIn[1] * velIn[1];
|
||||
|
||||
if (std::fabsf(normal[2]) < 0.001f || lengthSquared2D == 0.0)
|
||||
{
|
||||
velOut[0] = velIn[0];
|
||||
velOut[1] = velIn[1];
|
||||
velOut[2] = velIn[2];
|
||||
return;
|
||||
}
|
||||
|
||||
auto newZ = velIn[0] * normal[0] + velIn[1] * normal[1];
|
||||
newZ = -newZ / normal[2];
|
||||
const auto lengthScale = std::sqrtf((velIn[2] * velIn[2] + lengthSquared2D)
|
||||
/ (newZ * newZ + lengthSquared2D));
|
||||
|
||||
if (Movement::BGBouncesAllAngles.get<bool>()
|
||||
|| (lengthScale < 1.f || newZ < 0.f || velIn[2] > 0.f))
|
||||
{
|
||||
velOut[0] = velIn[0] * lengthScale;
|
||||
velOut[1] = velIn[1] * lengthScale;
|
||||
velOut[2] = newZ * lengthScale;
|
||||
}
|
||||
}
|
||||
|
||||
// Double bounces
|
||||
void Movement::Jump_ClearState_Hk(Game::playerState_s* ps)
|
||||
{
|
||||
if (Movement::BGBounces->current.integer != Movement::DOUBLE)
|
||||
{
|
||||
Game::Jump_ClearState(ps);
|
||||
}
|
||||
}
|
||||
|
||||
Game::gentity_s* Movement::Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex,
|
||||
float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool a7)
|
||||
{
|
||||
auto* result = Game::Weapon_RocketLauncher_Fire(ent, weaponIndex, spread, wp, gunVel, lockParms, a7);
|
||||
|
||||
if (ent->client != nullptr && BGRocketJump.get<bool>())
|
||||
{
|
||||
ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * 64.0f;
|
||||
ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * 64.0f;
|
||||
ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * 64.0f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Movement::StuckInClient_Hk(Game::gentity_s* self)
|
||||
{
|
||||
if (Movement::BGPlayerEjection.get<bool>())
|
||||
{
|
||||
return Utils::Hook::Call<int(Game::gentity_s*)>(0x402D30)(self); // StuckInClient
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Movement::CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end,
|
||||
const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles)
|
||||
{
|
||||
if (Movement::BGPlayerCollision.get<bool>())
|
||||
{
|
||||
Utils::Hook::Call<void(Game::trace_t*, const float*, const float*,
|
||||
const Game::Bounds*, const Game::Bounds*, int, const float*, const float*)>
|
||||
(0x478300)
|
||||
(results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* Movement::Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value,
|
||||
float min, float max, unsigned __int16 /*flags*/, const char* description)
|
||||
{
|
||||
Movement::PlayerSpectateSpeedScale = Dvar::Register<float>(dvarName, value,
|
||||
min, max, Game::DVAR_CHEAT | Game::DVAR_CODINFO, description);
|
||||
|
||||
return Movement::PlayerSpectateSpeedScale.get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
Movement::Movement()
|
||||
{
|
||||
Dvar::OnInit([]
|
||||
{
|
||||
static const char* bg_bouncesValues[] =
|
||||
{
|
||||
"disabled",
|
||||
"enabled",
|
||||
"double",
|
||||
nullptr
|
||||
};
|
||||
|
||||
Movement::PlayerDuckedSpeedScale = Game::Dvar_RegisterFloat("player_duckedSpeedScale",
|
||||
0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The scale applied to the player speed when ducking");
|
||||
|
||||
Movement::PlayerProneSpeedScale = Game::Dvar_RegisterFloat("player_proneSpeedScale",
|
||||
0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The scale applied to the player speed when crawling");
|
||||
|
||||
// 3arc naming convention
|
||||
Movement::CGUfoScaler = Dvar::Register<float>("cg_ufo_scaler",
|
||||
6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The speed at which ufo camera moves");
|
||||
|
||||
Movement::CGNoclipScaler = Dvar::Register<float>("cg_noclip_scaler",
|
||||
3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||
"The speed at which noclip camera moves");
|
||||
|
||||
Movement::BGBounces = Game::Dvar_RegisterEnum("bg_bounces",
|
||||
bg_bouncesValues, Movement::DISABLED, Game::DVAR_CODINFO, "Bounce glitch settings");
|
||||
|
||||
Movement::BGBouncesAllAngles = Dvar::Register<bool>("bg_bouncesAllAngles",
|
||||
false, Game::DVAR_CODINFO, "Force bounce from all angles");
|
||||
|
||||
Movement::BGRocketJump = Dvar::Register<bool>("bg_rocketJump",
|
||||
false, Game::DVAR_CODINFO, "Enable CoD4 rocket jumps");
|
||||
|
||||
Movement::BGPlayerEjection = Dvar::Register<bool>("bg_playerEjection",
|
||||
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
||||
|
||||
Movement::BGPlayerCollision = Dvar::Register<bool>("bg_playerCollision",
|
||||
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
||||
});
|
||||
|
||||
// Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used.
|
||||
Utils::Hook(0x448990, Movement::Dvar_RegisterSpectateSpeedScale, HOOK_CALL).install()->quick();
|
||||
|
||||
// PM_CmdScaleForStance
|
||||
Utils::Hook(0x572D9B, Movement::PM_PlayerDuckedSpeedScaleStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x572DA5, Movement::PM_PlayerProneSpeedScaleStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Hook PM_MoveScale so we can add custom speed scale for Ufo and Noclip
|
||||
Utils::Hook(0x56F845, Movement::PM_MoveScaleStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x56FABD, Movement::PM_MoveScaleStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Bounce logic
|
||||
Utils::Hook(0x4B1B2D, Movement::PM_StepSlideMoveStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x57383E, Movement::Jump_ClearState_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4B1B97, Movement::PM_ProjectVelocityStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Rocket jump
|
||||
Utils::Hook(0x4A4F9B, Movement::Weapon_RocketLauncher_Fire_Hk, HOOK_CALL).install()->quick(); // FireWeapon
|
||||
|
||||
// Hook StuckInClient & CM_TransformedCapsuleTrace
|
||||
// so we can prevent intersecting players from being pushed away from each other
|
||||
Utils::Hook(0x5D8153, Movement::StuckInClient_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x45A5BF, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity
|
||||
Utils::Hook(0x5A0CAD, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity
|
||||
}
|
||||
}
|
||||
|
@ -2,45 +2,43 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Movement : public Component
|
||||
{
|
||||
public:
|
||||
Movement();
|
||||
class Movement : public Component
|
||||
{
|
||||
public:
|
||||
Movement();
|
||||
|
||||
private:
|
||||
enum BouncesSettings { DISABLED, ENABLED, DOUBLE };
|
||||
private:
|
||||
enum BouncesSettings { DISABLED, ENABLED, DOUBLE };
|
||||
|
||||
static Dvar::Var PlayerDuckedSpeedScale;
|
||||
static Dvar::Var PlayerLastStandCrawlSpeedScale;
|
||||
static Dvar::Var PlayerProneSpeedScale;
|
||||
static Dvar::Var PlayerSpectateSpeedScale;
|
||||
static Dvar::Var CGUfoScaler;
|
||||
static Dvar::Var CGNoclipScaler;
|
||||
static Dvar::Var BGBouncesAllAngles;
|
||||
static Dvar::Var BGRocketJump;
|
||||
static Dvar::Var BGPlayerEjection;
|
||||
static Dvar::Var BGPlayerCollision;
|
||||
// Can't use Var class inside assembly stubs
|
||||
static Game::dvar_t* BGBounces;
|
||||
static Dvar::Var PlayerSpectateSpeedScale;
|
||||
static Dvar::Var CGUfoScaler;
|
||||
static Dvar::Var CGNoclipScaler;
|
||||
static Dvar::Var BGBouncesAllAngles;
|
||||
static Dvar::Var BGRocketJump;
|
||||
static Dvar::Var BGPlayerEjection;
|
||||
static Dvar::Var BGPlayerCollision;
|
||||
// Can't use Var class inside assembly stubs
|
||||
static Game::dvar_t* BGBounces;
|
||||
static Game::dvar_t* PlayerDuckedSpeedScale;
|
||||
static Game::dvar_t* PlayerProneSpeedScale;
|
||||
|
||||
static float PM_CmdScaleForStance(const Game::pmove_s* move);
|
||||
static void PM_CmdScaleForStanceStub();
|
||||
static void PM_PlayerDuckedSpeedScaleStub();
|
||||
static void PM_PlayerProneSpeedScaleStub();
|
||||
|
||||
static float PM_MoveScale(Game::playerState_s* ps, float forwardmove, float rightmove, float upmove);
|
||||
static void PM_MoveScaleStub();
|
||||
static float PM_MoveScale(Game::playerState_s* ps, float fmove, float rmove, float umove);
|
||||
static void PM_MoveScaleStub();
|
||||
|
||||
// Bounce logic
|
||||
static void PM_StepSlideMoveStub();
|
||||
static void PM_ProjectVelocityStub(const float* velIn, const float* normal, float* velOut);
|
||||
static void Jump_ClearState_Hk(Game::playerState_s* ps);
|
||||
// Bounce logic
|
||||
static void PM_StepSlideMoveStub();
|
||||
static void PM_ProjectVelocityStub(const float* velIn, const float* normal, float* velOut);
|
||||
static void Jump_ClearState_Hk(Game::playerState_s* ps);
|
||||
|
||||
static Game::gentity_s* Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex, float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool a7);
|
||||
static Game::gentity_s* Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex, float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool a7);
|
||||
|
||||
// Player collison
|
||||
static int StuckInClient_Hk(Game::gentity_s* self);
|
||||
static void CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles);
|
||||
// Player collison
|
||||
static int StuckInClient_Hk(Game::gentity_s* self);
|
||||
static void CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles);
|
||||
|
||||
static Game::dvar_t* Dvar_RegisterLastStandSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description);
|
||||
static Game::dvar_t* Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description);
|
||||
};
|
||||
static Game::dvar_t* Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description);
|
||||
};
|
||||
}
|
||||
|
@ -10,48 +10,59 @@ namespace Components
|
||||
{
|
||||
Game::NET_StringToAdr(addrString.data(), &this->address);
|
||||
}
|
||||
|
||||
Network::Address::Address(sockaddr* addr)
|
||||
{
|
||||
Game::SockadrToNetadr(addr, &this->address);
|
||||
}
|
||||
|
||||
bool Network::Address::operator==(const Network::Address& obj) const
|
||||
{
|
||||
return Game::NET_CompareAdr(this->address, obj.address);
|
||||
}
|
||||
|
||||
void Network::Address::setPort(unsigned short port)
|
||||
{
|
||||
this->address.port = htons(port);
|
||||
}
|
||||
|
||||
unsigned short Network::Address::getPort()
|
||||
{
|
||||
return ntohs(this->address.port);
|
||||
}
|
||||
|
||||
void Network::Address::setIP(DWORD ip)
|
||||
{
|
||||
this->address.ip.full = ip;
|
||||
}
|
||||
|
||||
void Network::Address::setIP(Game::netIP_t ip)
|
||||
{
|
||||
this->address.ip = ip;
|
||||
}
|
||||
|
||||
Game::netIP_t Network::Address::getIP()
|
||||
{
|
||||
return this->address.ip;
|
||||
}
|
||||
|
||||
void Network::Address::setType(Game::netadrtype_t type)
|
||||
{
|
||||
this->address.type = type;
|
||||
}
|
||||
|
||||
Game::netadrtype_t Network::Address::getType()
|
||||
{
|
||||
return this->address.type;
|
||||
}
|
||||
|
||||
sockaddr Network::Address::getSockAddr()
|
||||
{
|
||||
sockaddr addr;
|
||||
this->toSockAddr(&addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void Network::Address::toSockAddr(sockaddr* addr)
|
||||
{
|
||||
if (addr)
|
||||
@ -59,22 +70,27 @@ namespace Components
|
||||
Game::NetadrToSockadr(&this->address, addr);
|
||||
}
|
||||
}
|
||||
|
||||
void Network::Address::toSockAddr(sockaddr_in* addr)
|
||||
{
|
||||
this->toSockAddr(reinterpret_cast<sockaddr*>(addr));
|
||||
}
|
||||
|
||||
Game::netadr_t* Network::Address::get()
|
||||
{
|
||||
return &this->address;
|
||||
}
|
||||
|
||||
const char* Network::Address::getCString() const
|
||||
{
|
||||
return Game::NET_AdrToString(this->address);
|
||||
}
|
||||
|
||||
std::string Network::Address::getString() const
|
||||
{
|
||||
return this->getCString();
|
||||
}
|
||||
|
||||
bool Network::Address::isLocal()
|
||||
{
|
||||
// According to: https://en.wikipedia.org/wiki/Private_network
|
||||
@ -95,6 +111,7 @@ namespace Components
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Network::Address::isSelf()
|
||||
{
|
||||
if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback
|
||||
@ -110,6 +127,7 @@ namespace Components
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Network::Address::isLoopback()
|
||||
{
|
||||
if (this->getIP().full == 0x100007f) // 127.0.0.1
|
||||
@ -119,10 +137,12 @@ namespace Components
|
||||
|
||||
return Game::NET_IsLocalAddress(this->address);
|
||||
}
|
||||
|
||||
bool Network::Address::isValid()
|
||||
{
|
||||
return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP);
|
||||
}
|
||||
|
||||
void Network::Handle(const std::string& packet, Utils::Slot<Network::Callback> callback)
|
||||
{
|
||||
Network::PacketHandlers[Utils::String::ToLower(packet)] = callback;
|
||||
|
@ -167,6 +167,7 @@ namespace Components
|
||||
|
||||
void Node::RunFrame()
|
||||
{
|
||||
if (ServerList::useMasterServer) return;
|
||||
if (Dedicated::IsEnabled() && Dvar::Var("sv_lanOnly").get<bool>()) return;
|
||||
|
||||
if (!Dedicated::IsEnabled() && *Game::clcState > 0)
|
||||
@ -246,7 +247,7 @@ namespace Components
|
||||
|
||||
if (list.isnode() && (!list.port() || list.port() == address.getPort()))
|
||||
{
|
||||
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && list.protocol() == PROTOCOL)
|
||||
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::useMasterServer && list.protocol() == PROTOCOL)
|
||||
{
|
||||
NODE_LOG("Inserting %s into the serverlist\n", address.getCString());
|
||||
ServerList::InsertRequest(address);
|
||||
|
@ -307,7 +307,7 @@ namespace Components
|
||||
{
|
||||
int botCount = 0;
|
||||
int clientCount = 0;
|
||||
int maxclientCount = *Game::svs_numclients;
|
||||
int maxclientCount = *Game::svs_clientCount;
|
||||
|
||||
if (maxclientCount)
|
||||
{
|
||||
@ -315,7 +315,7 @@ namespace Components
|
||||
{
|
||||
if (Game::svs_clients[i].state >= 3)
|
||||
{
|
||||
if (Game::svs_clients[i].isBot) ++botCount;
|
||||
if (Game::svs_clients[i].bIsTestClient) ++botCount;
|
||||
else ++clientCount;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace Components
|
||||
{
|
||||
if (!sv_allowColoredNames.get<bool>())
|
||||
{
|
||||
char nameBuffer[64] = { 0 };
|
||||
char nameBuffer[64] = {0};
|
||||
TextRenderer::StripColors(name, nameBuffer, sizeof(nameBuffer));
|
||||
TextRenderer::StripAllTextIcons(nameBuffer, buffer, size);
|
||||
}
|
||||
@ -26,12 +26,12 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void PlayerName::ClientUserinfoChanged()
|
||||
__declspec(naked) void PlayerName::ClientCleanName()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax, [esp + 4h] // length
|
||||
//sub eax, 1
|
||||
|
||||
push eax
|
||||
|
||||
push ecx // name
|
||||
@ -53,12 +53,57 @@ namespace Components
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char* PlayerName::CleanStrStub(char* string)
|
||||
{
|
||||
TextRenderer::StripColors(string, string, strlen(string) + 1);
|
||||
return string;
|
||||
}
|
||||
|
||||
bool PlayerName::CopyClientNameCheck(char* dest, const char* source, int size)
|
||||
{
|
||||
Utils::Hook::Call<void(char*, const char*, int)>(0x4D6F80)(dest, source, size); // I_strncpyz
|
||||
|
||||
auto i = 0;
|
||||
while (i < size - 1 && dest[i] != '\0')
|
||||
{
|
||||
if (dest[i] > 125 || dest[i] < 32 || dest[i] == '%')
|
||||
{
|
||||
return false; // Illegal string
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
__declspec(naked) void PlayerName::SV_UserinfoChangedStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
call CopyClientNameCheck
|
||||
test al, al
|
||||
|
||||
jnz returnSafe
|
||||
|
||||
pushad
|
||||
|
||||
push 1 // tellThem
|
||||
push INVALID_NAME_MSG // reason
|
||||
push edi // drop
|
||||
mov eax, 0x4D1600 // SV_DropClient
|
||||
call eax
|
||||
add esp, 0xC
|
||||
|
||||
popad
|
||||
|
||||
returnSafe:
|
||||
push 0x401988
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
PlayerName::PlayerName()
|
||||
{
|
||||
sv_allowColoredNames = Dvar::Register<bool>("sv_allowColoredNames", true, Game::dvar_flag::DVAR_NONE, "Allow colored names on the server");
|
||||
@ -66,13 +111,17 @@ namespace Components
|
||||
// Disable SV_UpdateUserinfo_f, to block changing the name ingame
|
||||
Utils::Hook::Set<BYTE>(0x6258D0, 0xC3);
|
||||
|
||||
// Allow colored names ingame
|
||||
Utils::Hook(0x5D8B40, ClientUserinfoChanged, HOOK_JUMP).install()->quick();
|
||||
// Allow colored names ingame. Hook placed in ClientUserinfoChanged
|
||||
Utils::Hook(0x5D8B40, ClientCleanName, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Though, don't apply that to overhead names.
|
||||
Utils::Hook(0x581932, GetClientName, HOOK_CALL).install()->quick();
|
||||
|
||||
// Patch I_CleanStr
|
||||
Utils::Hook(0x4AD470, CleanStrStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Detect invalid characters including '%' to prevent format string vulnerabilities.
|
||||
// Kicks the player as soon as possible
|
||||
Utils::Hook(0x401983, SV_UserinfoChangedStub, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,14 @@ namespace Components
|
||||
|
||||
private:
|
||||
static Dvar::Var sv_allowColoredNames;
|
||||
// Message used when kicking players
|
||||
static constexpr auto INVALID_NAME_MSG = "Invalid name detected";
|
||||
|
||||
static char* CleanStrStub(char* string);
|
||||
static void ClientUserinfoChanged();
|
||||
static void ClientCleanName();
|
||||
static char* GetClientName(int localClientNum, int index, char* buf, size_t size);
|
||||
|
||||
static bool CopyClientNameCheck(char* dest, const char* source, int size);
|
||||
static void SV_UserinfoChangedStub();
|
||||
};
|
||||
}
|
||||
|
@ -47,62 +47,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
int QuickPatch::MsgReadBitsCompressCheckSV(const char *from, char *to, int size)
|
||||
{
|
||||
static char buffer[0x8000];
|
||||
|
||||
if (size > 0x800) return 0;
|
||||
size = Game::MSG_ReadBitsCompress(from, buffer, size);
|
||||
|
||||
if (size > 0x800) return 0;
|
||||
std::memcpy(to, buffer, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int QuickPatch::MsgReadBitsCompressCheckCL(const char *from, char *to, int size)
|
||||
{
|
||||
static char buffer[0x100000];
|
||||
|
||||
if (size > 0x20000) return 0;
|
||||
size = Game::MSG_ReadBitsCompress(from, buffer, size);
|
||||
|
||||
if (size > 0x20000) return 0;
|
||||
std::memcpy(to, buffer, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int QuickPatch::SVCanReplaceServerCommand(Game::client_t* /*client*/, const char* /*cmd*/)
|
||||
{
|
||||
// This is a fix copied from V2. As I don't have time to investigate, let's simply trust them
|
||||
return -1;
|
||||
}
|
||||
|
||||
long QuickPatch::AtolAdjustPlayerLimit(const char* string)
|
||||
{
|
||||
return std::min(atol(string), 18l);
|
||||
}
|
||||
|
||||
void QuickPatch::SelectStringTableEntryInDvarStub()
|
||||
{
|
||||
Command::ClientParams params;
|
||||
|
||||
if (params.size() >= 4)
|
||||
{
|
||||
const auto* dvarName = params[3];
|
||||
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
||||
|
||||
if (Command::Find(dvarName) ||
|
||||
(dvar != nullptr && dvar->flags & (Game::DVAR_WRITEPROTECTED | Game::DVAR_CHEAT | Game::DVAR_READONLY)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::CL_SelectStringTableEntryInDvar_f();
|
||||
}
|
||||
|
||||
__declspec(naked) void QuickPatch::JavelinResetHookStub()
|
||||
{
|
||||
__asm
|
||||
@ -117,69 +61,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) int QuickPatch::G_GetClientScore()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax, [esp + 4] // index
|
||||
mov ecx, ds : 1A831A8h // level: &g_clients
|
||||
|
||||
test ecx, ecx;
|
||||
jz invalid_ptr;
|
||||
|
||||
imul eax, 366Ch
|
||||
mov eax, [eax + ecx + 3134h]
|
||||
ret
|
||||
|
||||
invalid_ptr:
|
||||
xor eax, eax
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
bool QuickPatch::InvalidNameCheck(char* dest, const char* source, int size)
|
||||
{
|
||||
Utils::Hook::Call<void(char*, const char*, int)>(0x4D6F80)(dest, source, size); // I_strncpyz
|
||||
|
||||
for (int i = 0; i < size - 1; i++)
|
||||
{
|
||||
if (!dest[i]) break;
|
||||
|
||||
if (dest[i] > 125 || dest[i] < 32 || dest[i] == '%')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
__declspec(naked) void QuickPatch::InvalidNameStub()
|
||||
{
|
||||
static const char* kick_reason = "Invalid name detected.";
|
||||
|
||||
__asm
|
||||
{
|
||||
call InvalidNameCheck;
|
||||
test al, al
|
||||
|
||||
jnz returnSafe;
|
||||
|
||||
pushad;
|
||||
push 1;
|
||||
push kick_reason;
|
||||
push edi;
|
||||
mov eax, 0x004D1600; // SV_DropClientInternal
|
||||
call eax;
|
||||
add esp, 12;
|
||||
popad;
|
||||
|
||||
returnSafe:
|
||||
push 0x00401988;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::g_antilag;
|
||||
__declspec(naked) void QuickPatch::ClientEventsFireWeaponStub()
|
||||
{
|
||||
@ -378,9 +259,6 @@ namespace Components
|
||||
Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeaponStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x5D6D6A, QuickPatch::ClientEventsFireWeaponMeleeStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Disallow invalid player names
|
||||
Utils::Hook(0x401983, QuickPatch::InvalidNameStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Javelin fix
|
||||
Utils::Hook(0x578F52, QuickPatch::JavelinResetHookStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
@ -644,21 +522,6 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
// Exploit fixes
|
||||
Utils::Hook::Set<BYTE>(0x412370, 0xC3); // SV_SteamAuthClient
|
||||
Utils::Hook::Set<BYTE>(0x5A8C70, 0xC3); // CL_HandleRelayPacket
|
||||
Utils::Hook(0x414D92, QuickPatch::MsgReadBitsCompressCheckSV, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommands
|
||||
Utils::Hook(0x4A9F56, QuickPatch::MsgReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
|
||||
Utils::Hook(0x407376, QuickPatch::SVCanReplaceServerCommand , HOOK_CALL).install()->quick(); // SV_CanReplaceServerCommand
|
||||
Utils::Hook(0x5B67ED, QuickPatch::AtolAdjustPlayerLimit , HOOK_CALL).install()->quick(); // PartyHost_HandleJoinPartyRequest
|
||||
Utils::Hook::Nop(0x41698E, 5); // Disable Svcmd_EntityList_f
|
||||
|
||||
// Patch selectStringTableEntryInDvar
|
||||
Utils::Hook::Set(0x405959, QuickPatch::SelectStringTableEntryInDvarStub);
|
||||
|
||||
// Patch G_GetClientScore for uninitialised game
|
||||
Utils::Hook(0x469AC0, QuickPatch::G_GetClientScore, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Ignore call to print 'Offhand class mismatch when giving weapon...'
|
||||
Utils::Hook(0x5D9047, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
|
||||
@ -858,9 +721,6 @@ namespace Components
|
||||
});
|
||||
#endif
|
||||
|
||||
// Dvars
|
||||
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_ARCHIVE, "Stream friendly UI");
|
||||
|
||||
// Debug patches
|
||||
#ifdef DEBUG
|
||||
// ui_debugMode 1
|
||||
|
@ -12,21 +12,8 @@ namespace Components
|
||||
static void UnlockStats();
|
||||
|
||||
private:
|
||||
static void SelectStringTableEntryInDvarStub();
|
||||
|
||||
static int SVCanReplaceServerCommand(Game::client_t *client, const char *cmd);
|
||||
static int G_GetClientScore();
|
||||
|
||||
static int MsgReadBitsCompressCheckSV(const char *from, char *to, int size);
|
||||
static int MsgReadBitsCompressCheckCL(const char *from, char *to, int size);
|
||||
|
||||
static long AtolAdjustPlayerLimit(const char* string);
|
||||
|
||||
static void JavelinResetHookStub();
|
||||
|
||||
static bool InvalidNameCheck(char* dest, const char* source, int size);
|
||||
static void InvalidNameStub();
|
||||
|
||||
static Dvar::Var r_customAspectRatio;
|
||||
static Game::dvar_t* Dvar_RegisterAspectRatioDvar(const char* dvarName, const char** valueList, int defaultIndex, unsigned __int16 flags, const char* description);
|
||||
static void SetAspectRatioStub();
|
||||
|
161
src/Components/Modules/RawMouse.cpp
Normal file
161
src/Components/Modules/RawMouse.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Dvar::Var RawMouse::M_RawInput;
|
||||
int RawMouse::MouseRawX = 0;
|
||||
int RawMouse::MouseRawY = 0;
|
||||
|
||||
void RawMouse::IN_ClampMouseMove()
|
||||
{
|
||||
tagRECT rc;
|
||||
tagPOINT curPos;
|
||||
|
||||
GetCursorPos(&curPos);
|
||||
GetWindowRect(Window::GetWindow(), &rc);
|
||||
auto isClamped = false;
|
||||
if (curPos.x >= rc.left)
|
||||
{
|
||||
if (curPos.x >= rc.right)
|
||||
{
|
||||
curPos.x = rc.right - 1;
|
||||
isClamped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
curPos.x = rc.left;
|
||||
isClamped = true;
|
||||
}
|
||||
if (curPos.y >= rc.top)
|
||||
{
|
||||
if (curPos.y >= rc.bottom)
|
||||
{
|
||||
curPos.y = rc.bottom - 1;
|
||||
isClamped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
curPos.y = rc.top;
|
||||
isClamped = true;
|
||||
}
|
||||
|
||||
if (isClamped)
|
||||
{
|
||||
SetCursorPos(curPos.x, curPos.y);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL RawMouse::OnRawInput(LPARAM lParam, WPARAM)
|
||||
{
|
||||
auto dwSize = sizeof(RAWINPUT);
|
||||
static BYTE lpb[sizeof(RAWINPUT)];
|
||||
|
||||
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
|
||||
|
||||
auto* raw = reinterpret_cast<RAWINPUT*>(lpb);
|
||||
if (raw->header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
// Is there's really absolute mouse on earth?
|
||||
if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
|
||||
{
|
||||
MouseRawX = raw->data.mouse.lLastX;
|
||||
MouseRawY = raw->data.mouse.lLastY;
|
||||
}
|
||||
else
|
||||
{
|
||||
MouseRawX += raw->data.mouse.lLastX;
|
||||
MouseRawY += raw->data.mouse.lLastY;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void RawMouse::IN_RawMouseMove()
|
||||
{
|
||||
static auto r_fullscreen = Dvar::Var("r_fullscreen");
|
||||
|
||||
if (GetForegroundWindow() == Window::GetWindow())
|
||||
{
|
||||
if (r_fullscreen.get<bool>())
|
||||
IN_ClampMouseMove();
|
||||
|
||||
static auto oldX = 0, oldY = 0;
|
||||
|
||||
auto dx = MouseRawX - oldX;
|
||||
auto dy = MouseRawY - oldY;
|
||||
|
||||
oldX = MouseRawX;
|
||||
oldY = MouseRawY;
|
||||
|
||||
// Don't use raw input for menu?
|
||||
// Because it needs to call the ScreenToClient
|
||||
tagPOINT curPos;
|
||||
GetCursorPos(&curPos);
|
||||
Game::s_wmv->oldPos = curPos;
|
||||
ScreenToClient(Window::GetWindow(), &curPos);
|
||||
|
||||
auto recenterMouse = Game::CL_MouseEvent(curPos.x, curPos.y, dx, dy);
|
||||
|
||||
if (recenterMouse)
|
||||
{
|
||||
Game::IN_RecenterMouse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RawMouse::IN_RawMouse_Init()
|
||||
{
|
||||
if (Window::GetWindow() && RawMouse::M_RawInput.get<bool>())
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Logger::Print("Raw Mouse Init.\n");
|
||||
#endif
|
||||
|
||||
RAWINPUTDEVICE Rid[1];
|
||||
Rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
|
||||
Rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
|
||||
Rid[0].dwFlags = RIDEV_INPUTSINK;
|
||||
Rid[0].hwndTarget = Window::GetWindow();
|
||||
|
||||
RegisterRawInputDevices(Rid, ARRAYSIZE(Rid), sizeof(Rid[0]));
|
||||
}
|
||||
}
|
||||
|
||||
void RawMouse::IN_Init()
|
||||
{
|
||||
Game::IN_Init();
|
||||
IN_RawMouse_Init();
|
||||
}
|
||||
|
||||
void RawMouse::IN_MouseMove()
|
||||
{
|
||||
if (RawMouse::M_RawInput.get<bool>())
|
||||
{
|
||||
IN_RawMouseMove();
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::IN_MouseMove();
|
||||
}
|
||||
}
|
||||
|
||||
RawMouse::RawMouse()
|
||||
{
|
||||
Utils::Hook(0x475E65, RawMouse::IN_MouseMove, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x475E8D, RawMouse::IN_MouseMove, HOOK_JUMP).install()->quick();
|
||||
|
||||
Utils::Hook(0x467C03, RawMouse::IN_Init, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x64D095, RawMouse::IN_Init, HOOK_JUMP).install()->quick();
|
||||
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
RawMouse::M_RawInput = Dvar::Register<bool>("m_rawinput", true, Game::dvar_flag::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled.");
|
||||
});
|
||||
|
||||
Window::OnWndMessage(WM_INPUT, RawMouse::OnRawInput);
|
||||
Window::OnCreate(RawMouse::IN_RawMouse_Init);
|
||||
}
|
||||
}
|
22
src/Components/Modules/RawMouse.hpp
Normal file
22
src/Components/Modules/RawMouse.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class RawMouse : public Component
|
||||
{
|
||||
public:
|
||||
RawMouse();
|
||||
|
||||
static void IN_MouseMove();
|
||||
|
||||
private:
|
||||
static Dvar::Var M_RawInput;
|
||||
static int MouseRawX, MouseRawY;
|
||||
|
||||
static void IN_ClampMouseMove();
|
||||
static BOOL OnRawInput(LPARAM lParam, WPARAM);
|
||||
static void IN_RawMouseMove();
|
||||
static void IN_RawMouse_Init();
|
||||
static void IN_Init();
|
||||
};
|
||||
}
|
@ -502,7 +502,7 @@ namespace Components
|
||||
// }
|
||||
// });
|
||||
|
||||
// Log broken materials
|
||||
// Log broken materials
|
||||
Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick();
|
||||
|
||||
|
@ -18,7 +18,6 @@ namespace Components
|
||||
static void OnDeviceRecoveryEnd(Utils::Slot<Scheduler::Callback> callback);
|
||||
static void OnDeviceRecoveryBegin(Utils::Slot<Scheduler::Callback> callback);
|
||||
|
||||
|
||||
private:
|
||||
static void FrameStub();
|
||||
|
||||
|
@ -4,29 +4,30 @@ namespace Components
|
||||
{
|
||||
std::string Script::ScriptName;
|
||||
std::vector<int> Script::ScriptHandles;
|
||||
std::vector<Script::Function> Script::ScriptFunctions;
|
||||
std::unordered_map<std::string, Game::BuiltinFunctionDef> Script::CustomScrFunctions;
|
||||
std::unordered_map<std::string, Game::BuiltinMethodDef> Script::CustomScrMethods;
|
||||
std::vector<std::string> Script::ScriptNameStack;
|
||||
unsigned short Script::FunctionName;
|
||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
||||
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
||||
std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
|
||||
const char* Script::ReplacedPos = 0;
|
||||
const char* Script::ReplacedPos = nullptr;
|
||||
int Script::LastFrameTime = -1;
|
||||
|
||||
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
||||
|
||||
void Script::FunctionError()
|
||||
{
|
||||
std::string funcName = Game::SL_ConvertToString(Script::FunctionName);
|
||||
const auto* funcName = Game::SL_ConvertToString(Script::FunctionName);
|
||||
|
||||
Game::Scr_ShutdownAllocNode();
|
||||
|
||||
Logger::Print(23, "\n");
|
||||
Logger::Print(23, "******* script compile error *******\n");
|
||||
Logger::Print(23, "Error: unknown function %s in %s\n", funcName.data(), Script::ScriptName.data());
|
||||
Logger::Print(23, "Error: unknown function %s in %s\n", funcName, Script::ScriptName.data());
|
||||
Logger::Print(23, "************************************\n");
|
||||
|
||||
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function %s\n%s\n\n", funcName.data(), Script::ScriptName.data());
|
||||
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function %s\n%s\n\n", funcName, Script::ScriptName.data());
|
||||
}
|
||||
|
||||
__declspec(naked) void Script::StoreFunctionNameStub()
|
||||
@ -45,6 +46,37 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Script::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage)
|
||||
{
|
||||
const auto developer = Dvar::Var("developer").get<int>();
|
||||
|
||||
// Allow error messages to be printed if developer mode is on
|
||||
// Should check scrVarPub.developer but it's absent
|
||||
// in this version of the game so let's check the dvar
|
||||
if (!Game::scrVmPub->terminal_error && !developer)
|
||||
return;
|
||||
|
||||
// If were are developing let's call RuntimeErrorInternal
|
||||
// scrVmPub.debugCode seems to be always false
|
||||
if (Game::scrVmPub->debugCode || Game::scrVarPub->developer_script)
|
||||
{
|
||||
Game::RuntimeErrorInternal(23, codePos, index, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print(23, "%s\n", msg);
|
||||
}
|
||||
|
||||
// Let's not throw error unless we have to
|
||||
if (Game::scrVmPub->terminal_error)
|
||||
{
|
||||
if (dialogMessage == nullptr)
|
||||
dialogMessage = "";
|
||||
|
||||
Logger::Error(Game::ERR_SCRIPT_DROP, "\x15script runtime error\n(see console for details)\n%s\n%s", msg, dialogMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void Script::StoreScriptName(const char* name)
|
||||
{
|
||||
Script::ScriptNameStack.push_back(Script::ScriptName);
|
||||
@ -108,11 +140,9 @@ namespace Components
|
||||
std::string buffer = script.getBuffer();
|
||||
Utils::String::Replace(buffer, "\t", " ");
|
||||
|
||||
int line = 1;
|
||||
int lineOffset = 0;
|
||||
int inlineOffset = 0;
|
||||
auto line = 1, lineOffset = 0, inlineOffset = 0;
|
||||
|
||||
for (unsigned int i = 0; i < buffer.size(); ++i)
|
||||
for (size_t i = 0; i < buffer.size(); ++i)
|
||||
{
|
||||
// Terminate line
|
||||
if (i == offset)
|
||||
@ -129,7 +159,7 @@ namespace Components
|
||||
if (buffer[i] == '\n')
|
||||
{
|
||||
++line;
|
||||
lineOffset = i; // Includes the line break!
|
||||
lineOffset = static_cast<int>(i); // Includes the line break!
|
||||
inlineOffset = 0;
|
||||
}
|
||||
else
|
||||
@ -141,7 +171,7 @@ namespace Components
|
||||
Logger::Print(23, "in file %s, line %d:", filename, line);
|
||||
Logger::Print(23, "%s\n", buffer.data() + lineOffset);
|
||||
|
||||
for (int i = 0; i < (inlineOffset - 1); ++i)
|
||||
for (auto i = 0; i < (inlineOffset - 1); ++i)
|
||||
{
|
||||
Logger::Print(23, " ");
|
||||
}
|
||||
@ -156,11 +186,11 @@ namespace Components
|
||||
|
||||
void Script::CompileError(unsigned int offset, const char* message, ...)
|
||||
{
|
||||
char msgbuf[1024] = { 0 };
|
||||
va_list v;
|
||||
va_start(v, message);
|
||||
_vsnprintf_s(msgbuf, sizeof(msgbuf), message, v);
|
||||
va_end(v);
|
||||
char msgbuf[1024] = {0};
|
||||
va_list va;
|
||||
va_start(va, message);
|
||||
_vsnprintf_s(msgbuf, _TRUNCATE, message, va);
|
||||
va_end(va);
|
||||
|
||||
Game::Scr_ShutdownAllocNode();
|
||||
|
||||
@ -180,7 +210,7 @@ namespace Components
|
||||
if (!Game::Scr_LoadScript(script.data()))
|
||||
{
|
||||
Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data());
|
||||
Logger::Error(Game::ERR_DROP, reinterpret_cast<char*>(0x70B810), script.data());
|
||||
Logger::Error(Game::ERR_DROP, reinterpret_cast<const char*>(0x70B810), script.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -188,7 +218,7 @@ namespace Components
|
||||
}
|
||||
|
||||
Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data());
|
||||
int handle = Game::Scr_GetFunctionHandle(script.data(), label.data());
|
||||
const auto handle = Game::Scr_GetFunctionHandle(script.data(), label.data());
|
||||
if (handle)
|
||||
{
|
||||
Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data());
|
||||
@ -201,7 +231,7 @@ namespace Components
|
||||
|
||||
void Script::LoadGameType()
|
||||
{
|
||||
for (auto handle : Script::ScriptHandles)
|
||||
for (const auto& handle : Script::ScriptHandles)
|
||||
{
|
||||
Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0));
|
||||
}
|
||||
@ -213,19 +243,23 @@ namespace Components
|
||||
{
|
||||
Script::ScriptHandles.clear();
|
||||
|
||||
auto list = FileSystem::GetFileList("scripts/", "gsc");
|
||||
const auto list = FileSystem::GetFileList("scripts/", "gsc");
|
||||
|
||||
for (auto file : list)
|
||||
{
|
||||
file = "scripts/" + file;
|
||||
file.insert(0, "scripts/");
|
||||
|
||||
if (Utils::String::EndsWith(file, ".gsc"))
|
||||
{
|
||||
file = file.substr(0, file.size() - 4);
|
||||
}
|
||||
|
||||
int handle = Script::LoadScriptAndLabel(file, "init");
|
||||
if (handle) Script::ScriptHandles.push_back(handle);
|
||||
auto handle = Script::LoadScriptAndLabel(file, "init");
|
||||
|
||||
if (handle)
|
||||
{
|
||||
Script::ScriptHandles.push_back(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle = Script::LoadScriptAndLabel(file, "main");
|
||||
@ -236,61 +270,72 @@ namespace Components
|
||||
Game::GScr_LoadGameTypeScript();
|
||||
}
|
||||
|
||||
void Script::AddFunction(const std::string& name, Game::scr_function_t function, bool isDev)
|
||||
void Script::AddFunction(const char* name, Game::BuiltinFunction func, int type)
|
||||
{
|
||||
for (auto i = Script::ScriptFunctions.begin(); i != Script::ScriptFunctions.end();)
|
||||
{
|
||||
if (i->getName() == name)
|
||||
{
|
||||
i = Script::ScriptFunctions.erase(i);
|
||||
continue;
|
||||
}
|
||||
Game::BuiltinFunctionDef toAdd;
|
||||
toAdd.actionString = name;
|
||||
toAdd.actionFunc = func;
|
||||
toAdd.type = type;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
Script::ScriptFunctions.push_back({ name, function, isDev });
|
||||
CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd);
|
||||
}
|
||||
|
||||
Game::scr_function_t Script::GetFunction(void* caller, const char** name, int* isDev)
|
||||
void Script::AddMethod(const char* name, Game::BuiltinMethod func, int type)
|
||||
{
|
||||
for (auto& function : Script::ScriptFunctions)
|
||||
{
|
||||
if (name && *name)
|
||||
{
|
||||
if (Utils::String::ToLower(*name) == Utils::String::ToLower(function.getName()))
|
||||
{
|
||||
*name = function.getName();
|
||||
*isDev = function.isDev();
|
||||
return function.getFunction();
|
||||
}
|
||||
}
|
||||
else if (caller == reinterpret_cast<void*>(0x465781))
|
||||
{
|
||||
Game::Scr_RegisterFunction(function.getFunction());
|
||||
}
|
||||
}
|
||||
Game::BuiltinMethodDef toAdd;
|
||||
toAdd.actionString = name;
|
||||
toAdd.actionFunc = func;
|
||||
toAdd.type = type;
|
||||
|
||||
return nullptr;
|
||||
CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd);
|
||||
}
|
||||
|
||||
__declspec(naked) void Script::GetFunctionStub()
|
||||
Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type)
|
||||
{
|
||||
__asm
|
||||
if (pName != nullptr)
|
||||
{
|
||||
test eax, eax
|
||||
jnz returnSafe
|
||||
const auto got = Script::CustomScrFunctions.find(Utils::String::ToLower(*pName));
|
||||
|
||||
sub esp, 8h
|
||||
push [esp + 10h]
|
||||
call Script::GetFunction
|
||||
add esp, 0Ch
|
||||
|
||||
returnSafe:
|
||||
pop edi
|
||||
pop esi
|
||||
retn
|
||||
// If no function was found let's call game's function
|
||||
if (got != Script::CustomScrFunctions.end())
|
||||
{
|
||||
*type = got->second.type;
|
||||
return got->second.actionFunc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& [name, builtin] : Script::CustomScrFunctions)
|
||||
{
|
||||
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data());
|
||||
}
|
||||
}
|
||||
|
||||
return Utils::Hook::Call<Game::BuiltinFunction(const char**, int*)>(0x5FA2B0)(pName, type); // BuiltIn_GetFunction
|
||||
}
|
||||
|
||||
Game::BuiltinMethod Script::BuiltIn_GetMethod(const char** pName, int* type)
|
||||
{
|
||||
if (pName != nullptr)
|
||||
{
|
||||
const auto got = Script::CustomScrMethods.find(Utils::String::ToLower(*pName));
|
||||
|
||||
// If no method was found let's call game's function
|
||||
if (got != Script::CustomScrMethods.end())
|
||||
{
|
||||
*type = got->second.type;
|
||||
return got->second.actionFunc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& [name, builtin] : Script::CustomScrMethods)
|
||||
{
|
||||
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data());
|
||||
}
|
||||
}
|
||||
|
||||
return Utils::Hook::Call<Game::BuiltinMethod(const char**, int*)>(0x5FA360)(pName, type); // Player_GetMethod
|
||||
}
|
||||
|
||||
void Script::StoreScriptBaseProgramNum()
|
||||
@ -300,14 +345,12 @@ namespace Components
|
||||
|
||||
void Script::Scr_PrintPrevCodePos(int scriptPos)
|
||||
{
|
||||
int bestCodePos = -1;
|
||||
int nextCodePos = -1;
|
||||
int offset = -1;
|
||||
auto bestCodePos = -1, nextCodePos = -1, offset = -1;
|
||||
std::string file;
|
||||
|
||||
for (auto kv : Script::ScriptBaseProgramNum)
|
||||
for (const auto& [key, value] : Script::ScriptBaseProgramNum)
|
||||
{
|
||||
int codePos = kv.first;
|
||||
const auto codePos = key;
|
||||
|
||||
if (codePos > scriptPos)
|
||||
{
|
||||
@ -322,17 +365,15 @@ namespace Components
|
||||
|
||||
bestCodePos = codePos;
|
||||
|
||||
file = kv.second;
|
||||
file = value;
|
||||
offset = scriptPos - bestCodePos;
|
||||
}
|
||||
|
||||
if (bestCodePos == -1)
|
||||
return;
|
||||
|
||||
float onehundred = 100.0;
|
||||
|
||||
Logger::Print(23, "\n@ %d (%d - %d)\n", scriptPos, bestCodePos, nextCodePos);
|
||||
Logger::Print(23, "in %s (%.1f%% through the source)\n\n", file.c_str(), ((offset * onehundred) / (nextCodePos - bestCodePos)));
|
||||
Logger::Print(23, "in %s (%.1f%% through the source)\n\n", file.data(), ((offset * 100.0f) / (nextCodePos - bestCodePos)));
|
||||
}
|
||||
|
||||
__declspec(naked) void Script::Scr_PrintPrevCodePosStub()
|
||||
@ -373,15 +414,15 @@ namespace Components
|
||||
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Script::ScriptBaseProgramNum.clear();
|
||||
Script::VMShutdownSignal.connect(callback);
|
||||
Script::VMShutdownSignal.connect(std::move(callback));
|
||||
}
|
||||
|
||||
void Script::ScrShutdownSystemStub(int num)
|
||||
void Script::ScrShutdownSystemStub(unsigned char sys)
|
||||
{
|
||||
Script::VMShutdownSignal();
|
||||
|
||||
// Scr_ShutdownSystem
|
||||
Utils::Hook::Call<void(int)>(0x421EE0)(num);
|
||||
Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys);
|
||||
}
|
||||
|
||||
unsigned int Script::SetExpFogStub()
|
||||
@ -403,7 +444,7 @@ namespace Components
|
||||
{
|
||||
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount)
|
||||
{
|
||||
Game::Scr_Error("^1GetCodePosForParam: Index is out of range!\n");
|
||||
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Index is out of range!\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -411,7 +452,7 @@ namespace Components
|
||||
|
||||
if (value->type != Game::VAR_FUNCTION)
|
||||
{
|
||||
Game::Scr_Error("^1GetCodePosForParam: Expects a function as parameter!\n");
|
||||
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Expects a function as parameter!\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -430,7 +471,7 @@ namespace Components
|
||||
{
|
||||
if (what[0] == '\0' || with[0] == '\0')
|
||||
{
|
||||
Logger::Print("Warning: Invalid paramters passed to ReplacedFunctions\n");
|
||||
Logger::Print("Warning: Invalid parameters passed to ReplacedFunctions\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -487,24 +528,28 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
Game::gentity_t* Script::getEntFromEntRef(Game::scr_entref_t entref)
|
||||
Game::client_t* Script::GetClient(const Game::gentity_t* ent)
|
||||
{
|
||||
Game::gentity_t* gentity = &Game::g_entities[entref];
|
||||
return gentity;
|
||||
}
|
||||
assert(ent != nullptr);
|
||||
|
||||
Game::client_t* Script::getClientFromEnt(Game::gentity_t* gentity)
|
||||
{
|
||||
if (!gentity->client)
|
||||
if (ent->client == nullptr)
|
||||
{
|
||||
Logger::Error(Game::ERR_SCRIPT_DROP, "Entity: %i is not a client", gentity);
|
||||
Game::Scr_ObjectError(Utils::String::VA("Entity %i is not a player", ent->s.number));
|
||||
return nullptr;
|
||||
}
|
||||
return &Game::svs_clients[gentity->s.number];
|
||||
|
||||
if (ent->s.number >= *Game::svs_clientCount)
|
||||
{
|
||||
Game::Scr_ObjectError(Utils::String::VA("Entity %i is out of bounds", ent->s.number));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &Game::svs_clients[ent->s.number];
|
||||
}
|
||||
|
||||
void Script::AddFunctions()
|
||||
{
|
||||
Script::AddFunction("ReplaceFunc", [](Game::scr_entref_t) // gsc: ReplaceFunc(<function>, <function>)
|
||||
Script::AddFunction("ReplaceFunc", []() // gsc: ReplaceFunc(<function>, <function>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 2u)
|
||||
{
|
||||
@ -519,7 +564,7 @@ namespace Components
|
||||
});
|
||||
|
||||
// System time
|
||||
Script::AddFunction("GetSystemTime", [](Game::scr_entref_t) // gsc: GetSystemTime()
|
||||
Script::AddFunction("GetSystemTime", []() // gsc: GetSystemTime()
|
||||
{
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
@ -527,7 +572,7 @@ namespace Components
|
||||
Game::Scr_AddInt(time.wSecond);
|
||||
});
|
||||
|
||||
Script::AddFunction("GetSystemMilliseconds", [](Game::scr_entref_t) // gsc: GetSystemMilliseconds()
|
||||
Script::AddFunction("GetSystemMilliseconds", []() // gsc: GetSystemMilliseconds()
|
||||
{
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
@ -535,106 +580,116 @@ namespace Components
|
||||
Game::Scr_AddInt(time.wMilliseconds);
|
||||
});
|
||||
|
||||
// Print to console, even without being in 'developer 1'.
|
||||
Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole(<string>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1PrintConsole: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto str = Game::Scr_GetString(0);
|
||||
|
||||
Game::Com_Printf(0, str);
|
||||
});
|
||||
|
||||
// Executes command to the console
|
||||
Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec(<string>)
|
||||
Script::AddFunction("Exec", []() // gsc: Exec(<string>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
const auto str = Game::Scr_GetString(0);
|
||||
|
||||
if (str == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1Exec: Needs one string parameter!\n");
|
||||
Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto str = Game::Scr_GetString(0);
|
||||
|
||||
Command::Execute(str, false);
|
||||
});
|
||||
|
||||
|
||||
// Script Storage Funcs
|
||||
Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(<str key>, <str data>);
|
||||
// Allow printing to the console even when developer is 0
|
||||
Script::AddFunction("PrintConsole", []() // gsc: PrintConsole(<string>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING)
|
||||
for (auto i = 0u; i < Game::Scr_GetNumParam(); i++)
|
||||
{
|
||||
Game::Scr_Error("^1StorageSet: Needs two string parameters!\n");
|
||||
return;
|
||||
const auto str = Game::Scr_GetString(i);
|
||||
|
||||
if (str == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::Print(Game::level->scriptPrintChannel, "%s", str);
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
std::string data = Game::Scr_GetString(1);
|
||||
|
||||
Script::ScriptStorage.insert_or_assign(key, data);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(<str key>);
|
||||
// Script Storage Functions
|
||||
Script::AddFunction("StorageSet", []() // gsc: StorageSet(<str key>, <str data>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
const auto* value = Game::Scr_GetString(1);
|
||||
|
||||
if (key == nullptr || value == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1StorageRemove: Needs one string parameter!\n");
|
||||
Game::Scr_Error("^1StorageSet: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
Script::ScriptStorage.insert_or_assign(key, value);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageRemove", []() // gsc: StorageRemove(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1StorageRemove: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Script::ScriptStorage.count(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key.c_str()));
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
||||
return;
|
||||
}
|
||||
|
||||
Script::ScriptStorage.erase(key);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(<str key>);
|
||||
Script::AddFunction("StorageGet", []() // gsc: StorageGet(<str key>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1StorageGet: Needs one string parameter!\n");
|
||||
Game::Scr_Error("^1StorageGet: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
if (!Script::ScriptStorage.count(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key.c_str()));
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = Script::ScriptStorage.at(key);
|
||||
Game::Scr_AddString(data.c_str());
|
||||
const auto& data = Script::ScriptStorage.at(key);
|
||||
Game::Scr_AddString(data.data());
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(<str key>);
|
||||
Script::AddFunction("StorageHas", []() // gsc: StorageHas(<str key>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1StorageHas: Needs one string parameter!\n");
|
||||
Game::Scr_Error("^1StorageHas: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
Game::Scr_AddInt(Script::ScriptStorage.count(key));
|
||||
Game::Scr_AddBool(static_cast<int>(Script::ScriptStorage.count(key))); // Until C++17
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear();
|
||||
Script::AddFunction("StorageClear", []() // gsc: StorageClear();
|
||||
{
|
||||
Script::ScriptStorage.clear();
|
||||
});
|
||||
|
||||
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
|
||||
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
|
||||
Game::Scr_AddBool((ent->client->flags & Game::PLAYER_FLAG_FROZEN) != 0);
|
||||
});
|
||||
}
|
||||
|
||||
Script::Script()
|
||||
@ -645,14 +700,12 @@ namespace Components
|
||||
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// enable scr_error printing if in developer
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
int developer = Dvar::Var("developer").get<int>();
|
||||
|
||||
if (developer > 0 && Dedicated::IsEnabled())
|
||||
Utils::Hook::Set<BYTE>(0x48D8C7, 0x75);
|
||||
});
|
||||
Utils::Hook(0x61E3AD, Script::RuntimeError, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x621976, Script::RuntimeError, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x62246E, Script::RuntimeError, HOOK_CALL).install()->quick();
|
||||
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
|
||||
// On IW5 the function is entirely nullsubbed
|
||||
Utils::Hook::Set<BYTE>(0x5F8DBF, 0xEB);
|
||||
|
||||
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
|
||||
@ -661,27 +714,29 @@ namespace Components
|
||||
Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x44E736, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetFunction
|
||||
Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod
|
||||
// Fetch custom functions
|
||||
Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction
|
||||
Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethod, HOOK_CALL).install()->quick(); // Scr_GetMethod
|
||||
|
||||
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook::Nop(0x61E933, 1);
|
||||
|
||||
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_LoadGame
|
||||
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_ShutdownGame
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
if (!Game::SV_Loaded())
|
||||
return;
|
||||
|
||||
int nowMs = Game::Sys_Milliseconds();
|
||||
const auto nowMs = Game::Sys_Milliseconds();
|
||||
|
||||
if (Script::LastFrameTime != -1)
|
||||
{
|
||||
int timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * Dvar::Var("timescale").get<float>());
|
||||
const auto timeScale = Dvar::Var("timescale").get<float>();
|
||||
const auto timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * timeScale);
|
||||
|
||||
if (timeTaken >= 500)
|
||||
Logger::Print(23, "Hitch warning: %i msec frame time\n", timeTaken);
|
||||
@ -690,10 +745,19 @@ namespace Components
|
||||
Script::LastFrameTime = nowMs;
|
||||
});
|
||||
|
||||
Script::AddFunction("debugBox", [](Game::scr_entref_t)
|
||||
#ifdef _DEBUG
|
||||
Script::AddFunction("DebugBox", []()
|
||||
{
|
||||
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0);
|
||||
}, true);
|
||||
const auto* message = Game::Scr_GetString(0);
|
||||
|
||||
if (message == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1DebugBox: Illegal parameter!\n");
|
||||
}
|
||||
|
||||
MessageBoxA(nullptr, message, "DEBUG", MB_OK);
|
||||
}, 1);
|
||||
#endif
|
||||
|
||||
Script::AddFunctions();
|
||||
|
||||
|
@ -6,36 +6,23 @@ namespace Components
|
||||
class Script : public Component
|
||||
{
|
||||
public:
|
||||
class Function
|
||||
{
|
||||
public:
|
||||
Function(const std::string& _name, Game::scr_function_t _callback, bool _dev) : name(_name), callback(_callback), dev(_dev) {}
|
||||
|
||||
const char* getName() const { return this->name.data(); }
|
||||
bool isDev() const { return this->dev; }
|
||||
Game::scr_function_t getFunction() const { return this->callback; }
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
Game::scr_function_t callback;
|
||||
bool dev;
|
||||
};
|
||||
|
||||
Script();
|
||||
~Script();
|
||||
|
||||
static int LoadScriptAndLabel(const std::string& script, const std::string& label);
|
||||
static void AddFunction(const std::string& name, Game::scr_function_t function, bool isDev = false);
|
||||
|
||||
static void AddFunction(const char* name, Game::BuiltinFunction func, int type = 0);
|
||||
static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0);
|
||||
|
||||
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback);
|
||||
|
||||
static Game::gentity_t* getEntFromEntRef(Game::scr_entref_t entref);
|
||||
static Game::client_t* getClientFromEnt(Game::gentity_t* gentity);
|
||||
static Game::client_t* GetClient(const Game::gentity_t* gentity);
|
||||
|
||||
private:
|
||||
static std::string ScriptName;
|
||||
static std::vector<int> ScriptHandles;
|
||||
static std::vector<Function> ScriptFunctions;
|
||||
static std::unordered_map<std::string, Game::BuiltinFunctionDef> CustomScrFunctions;
|
||||
static std::unordered_map<std::string, Game::BuiltinMethodDef> CustomScrMethods;
|
||||
static std::vector<std::string> ScriptNameStack;
|
||||
static unsigned short FunctionName;
|
||||
static std::unordered_map<std::string, std::string> ScriptStorage;
|
||||
@ -51,6 +38,7 @@ namespace Components
|
||||
|
||||
static void FunctionError();
|
||||
static void StoreFunctionNameStub();
|
||||
static void RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage);
|
||||
|
||||
static void StoreScriptName(const char* name);
|
||||
static void StoreScriptNameStub();
|
||||
@ -61,10 +49,10 @@ namespace Components
|
||||
static void LoadGameType();
|
||||
static void LoadGameTypeScript();
|
||||
|
||||
static Game::scr_function_t GetFunction(void* caller, const char** name, int* isDev);
|
||||
static void GetFunctionStub();
|
||||
static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type);
|
||||
static Game::BuiltinMethod BuiltIn_GetMethod(const char** pName, int* type);
|
||||
|
||||
static void ScrShutdownSystemStub(int);
|
||||
static void ScrShutdownSystemStub(unsigned char sys);
|
||||
static void StoreScriptBaseProgramNumStub();
|
||||
static void StoreScriptBaseProgramNum();
|
||||
static void Scr_PrintPrevCodePosStub();
|
||||
|
401
src/Components/Modules/ScriptExtension.cpp
Normal file
401
src/Components/Modules/ScriptExtension.cpp
Normal file
@ -0,0 +1,401 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
||||
|
||||
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
|
||||
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
||||
|
||||
void ScriptExtension::AddEntityField(const char* name, Game::fieldtype_t type,
|
||||
const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter)
|
||||
{
|
||||
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
ScriptExtension::CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void ScriptExtension::AddClientField(const char* name, Game::fieldtype_t type,
|
||||
const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter)
|
||||
{
|
||||
static std::uint16_t fieldOffsetStart = 21; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
||||
|
||||
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
||||
ScriptExtension::CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
||||
{
|
||||
for (const auto& [offset, field] : ScriptExtension::CustomEntityFields)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
||||
|
||||
for (const auto& [offset, field] : ScriptExtension::CustomClientFields)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
}
|
||||
|
||||
// Because some functions are inlined we have to hook this function instead of Scr_SetEntityField
|
||||
int ScriptExtension::Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset)
|
||||
{
|
||||
if (classnum == Game::ClassNum::CLASS_NUM_ENTITY)
|
||||
{
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
||||
if (got != ScriptExtension::CustomEntityFields.end())
|
||||
{
|
||||
got->second.setter(&Game::g_entities[entnum], offset);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// No custom generic field was found, let the game handle it
|
||||
return Game::Scr_SetObjectField(classnum, entnum, offset);
|
||||
}
|
||||
|
||||
// Offset was already converted to array 'index' following binop offset & ~Game::ENTFIELD_MASK
|
||||
void ScriptExtension::Scr_SetClientFieldStub(Game::gclient_s* client, int offset)
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
||||
if (got != ScriptExtension::CustomClientFields.end())
|
||||
{
|
||||
got->second.setter(client, &got->second);
|
||||
return;
|
||||
}
|
||||
|
||||
// No custom field client was found, let the game handle it
|
||||
Game::Scr_SetClientField(client, offset);
|
||||
}
|
||||
|
||||
void ScriptExtension::Scr_GetEntityFieldStub(int entnum, int offset)
|
||||
{
|
||||
if ((offset & Game::ENTFIELD_MASK) == Game::ENTFIELD_CLIENT)
|
||||
{
|
||||
// If we have a ENTFIELD_CLIENT offset we need to check g_entity is actually a fully connected client
|
||||
if (Game::g_entities[entnum].client != nullptr)
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||
|
||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
||||
if (got != ScriptExtension::CustomClientFields.end())
|
||||
{
|
||||
// Game functions probably don't ever need to use the reference to client_fields_s...
|
||||
got->second.getter(Game::g_entities[entnum].client, &got->second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular entity offsets can be searched directly in our custom handler
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
||||
if (got != ScriptExtension::CustomEntityFields.end())
|
||||
{
|
||||
got->second.getter(&Game::g_entities[entnum], offset);
|
||||
return;
|
||||
}
|
||||
|
||||
// No custom generic field was found, let the game handle it
|
||||
Game::Scr_GetEntityField(entnum, offset);
|
||||
}
|
||||
|
||||
void ScriptExtension::AddFunctions()
|
||||
{
|
||||
// File functions
|
||||
Script::AddFunction("FileWrite", []() // gsc: FileWrite(<filepath>, <string>, <mode>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
auto* text = Game::Scr_GetString(1);
|
||||
auto* mode = Game::Scr_GetString(2);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (text == nullptr || mode == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1FileWrite: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileWrite: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != "append"s && mode != "write"s)
|
||||
{
|
||||
Logger::Print("^3FileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||
mode = "write";
|
||||
}
|
||||
|
||||
if (mode == "write"s)
|
||||
{
|
||||
FileSystem::FileWriter(path).write(text);
|
||||
}
|
||||
else if (mode == "append"s)
|
||||
{
|
||||
FileSystem::FileWriter(path, true).write(text);
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("FileRead", []() // gsc: FileRead(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileRead: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileSystem::FileReader(path).exists())
|
||||
{
|
||||
Logger::Print("^1FileRead: file not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
|
||||
});
|
||||
|
||||
Script::AddFunction("FileExists", []() // gsc: FileExists(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileExists: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
|
||||
});
|
||||
|
||||
Script::AddFunction("FileRemove", []() // gsc: FileRemove(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileRemove: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileRemove: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto p = std::filesystem::path(path);
|
||||
const auto& folder = p.parent_path().string();
|
||||
const auto& file = p.filename().string();
|
||||
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
|
||||
});
|
||||
|
||||
// Misc functions
|
||||
Script::AddFunction("ToUpper", []() // gsc: ToUpper(<string>)
|
||||
{
|
||||
const auto scriptValue = Game::Scr_GetConstString(0);
|
||||
const auto* string = Game::SL_ConvertToString(scriptValue);
|
||||
|
||||
char out[1024] = {0}; // 1024 is the max for a string in this SL system
|
||||
bool changed = false;
|
||||
|
||||
size_t i = 0;
|
||||
while (i < sizeof(out))
|
||||
{
|
||||
const auto value = *string;
|
||||
const auto result = static_cast<char>(std::toupper(static_cast<unsigned char>(value)));
|
||||
out[i] = result;
|
||||
|
||||
if (value != result)
|
||||
changed = true;
|
||||
|
||||
if (result == '\0') // Finished converting string
|
||||
break;
|
||||
|
||||
++string;
|
||||
++i;
|
||||
}
|
||||
|
||||
// Null terminating character was overwritten
|
||||
if (i >= sizeof(out))
|
||||
{
|
||||
Game::Scr_Error("string too long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Game::Scr_AddString(out);
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::SL_AddRefToString(scriptValue);
|
||||
Game::Scr_AddConstString(scriptValue);
|
||||
Game::SL_RemoveRefToString(scriptValue);
|
||||
}
|
||||
});
|
||||
|
||||
// Func present on IW5
|
||||
Script::AddFunction("StrICmp", []() // gsc: StrICmp(<string>, <string>)
|
||||
{
|
||||
const auto value1 = Game::Scr_GetConstString(0);
|
||||
const auto value2 = Game::Scr_GetConstString(1);
|
||||
|
||||
const auto result = _stricmp(Game::SL_ConvertToString(value1),
|
||||
Game::SL_ConvertToString(value2));
|
||||
|
||||
Game::Scr_AddInt(result);
|
||||
});
|
||||
|
||||
// Func present on IW5
|
||||
Script::AddFunction("IsEndStr", []() // gsc: IsEndStr(<string>, <string>)
|
||||
{
|
||||
const auto* s1 = Game::Scr_GetString(0);
|
||||
const auto* s2 = Game::Scr_GetString(1);
|
||||
|
||||
if (s1 == nullptr || s2 == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1IsEndStr: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddBool(Utils::String::EndsWith(s1, s2));
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptExtension::AddMethods()
|
||||
{
|
||||
// ScriptExtension methods
|
||||
Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp()
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
std::string ip = Game::NET_AdrToString(client->netchan.remoteAddress);
|
||||
|
||||
if (const auto pos = ip.find_first_of(":"); pos != std::string::npos)
|
||||
ip.erase(ip.begin() + pos, ip.end()); // Erase port
|
||||
|
||||
Game::Scr_AddString(ip.data());
|
||||
});
|
||||
|
||||
Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing()
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
Game::Scr_AddInt(client->ping);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptExtension::Scr_TableLookupIStringByRow()
|
||||
{
|
||||
if (Game::Scr_GetNumParam() < 3)
|
||||
{
|
||||
Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* fileName = Game::Scr_GetString(0);
|
||||
const auto rowNum = Game::Scr_GetInt(1);
|
||||
const auto returnValueColumnNum = Game::Scr_GetInt(2);
|
||||
|
||||
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
|
||||
|
||||
if (table == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum);
|
||||
Game::Scr_AddIString(value);
|
||||
}
|
||||
|
||||
void ScriptExtension::AddEntityFields()
|
||||
{
|
||||
ScriptExtension::AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
ent->flags = Game::Scr_GetInt(0);
|
||||
},
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
Game::Scr_AddInt(ent->flags);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptExtension::AddClientFields()
|
||||
{
|
||||
ScriptExtension::AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||
{
|
||||
pSelf->flags = Game::Scr_GetInt(0);
|
||||
},
|
||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||
{
|
||||
Game::Scr_AddInt(pSelf->flags);
|
||||
});
|
||||
}
|
||||
|
||||
ScriptExtension::ScriptExtension()
|
||||
{
|
||||
ScriptExtension::AddFunctions();
|
||||
ScriptExtension::AddMethods();
|
||||
ScriptExtension::AddEntityFields();
|
||||
ScriptExtension::AddClientFields();
|
||||
|
||||
// Correct builtin function pointer
|
||||
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
|
||||
|
||||
Utils::Hook(0x4EC721, ScriptExtension::GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||
|
||||
Utils::Hook(0x41BED2, ScriptExtension::Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||
Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||
Utils::Hook(0x4FF413, ScriptExtension::Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||
}
|
||||
}
|
34
src/Components/Modules/ScriptExtension.hpp
Normal file
34
src/Components/Modules/ScriptExtension.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class ScriptExtension : public Component
|
||||
{
|
||||
public:
|
||||
ScriptExtension();
|
||||
|
||||
static void AddEntityField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter);
|
||||
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
||||
|
||||
private:
|
||||
static const char* QueryStrings[];
|
||||
|
||||
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
|
||||
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
||||
|
||||
static void GScr_AddFieldsForEntityStub();
|
||||
|
||||
// Two hooks because it makes our code cleaner (luckily functions were not inlined)
|
||||
static int Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset);
|
||||
static void Scr_SetClientFieldStub(Game::gclient_s* client, int offset);
|
||||
|
||||
// One hook because functions were inlined
|
||||
static void Scr_GetEntityFieldStub(int entnum, int offset);
|
||||
|
||||
static void AddFunctions();
|
||||
static void AddMethods();
|
||||
static void AddEntityFields();
|
||||
static void AddClientFields();
|
||||
static void Scr_TableLookupIStringByRow();
|
||||
};
|
||||
}
|
112
src/Components/Modules/Security.cpp
Normal file
112
src/Components/Modules/Security.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
int Security::MsgReadBitsCompressCheckSV(const char* from, char* to, int size)
|
||||
{
|
||||
static char buffer[0x8000];
|
||||
|
||||
if (size > 0x800) return 0;
|
||||
size = Game::MSG_ReadBitsCompress(from, buffer, size);
|
||||
|
||||
if (size > 0x800) return 0;
|
||||
std::memcpy(to, buffer, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int Security::MsgReadBitsCompressCheckCL(const char* from, char* to, int size)
|
||||
{
|
||||
static char buffer[0x100000];
|
||||
|
||||
if (size > 0x20000) return 0;
|
||||
size = Game::MSG_ReadBitsCompress(from, buffer, size);
|
||||
|
||||
if (size > 0x20000) return 0;
|
||||
std::memcpy(to, buffer, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int Security::SVCanReplaceServerCommand(Game::client_t* /*client*/, const char* /*cmd*/)
|
||||
{
|
||||
// This is a fix copied from V2. As I don't have time to investigate, let's simply trust them
|
||||
return -1;
|
||||
}
|
||||
|
||||
long Security::AtolAdjustPlayerLimit(const char* string)
|
||||
{
|
||||
return std::min<long>(std::atol(string), 18);
|
||||
}
|
||||
|
||||
void Security::SelectStringTableEntryInDvarStub()
|
||||
{
|
||||
Command::ClientParams params;
|
||||
|
||||
if (params.size() >= 4)
|
||||
{
|
||||
const auto* dvarName = params[3];
|
||||
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
||||
|
||||
if (Command::Find(dvarName) ||
|
||||
(dvar != nullptr && dvar->flags & (Game::DVAR_WRITEPROTECTED | Game::DVAR_CHEAT | Game::DVAR_READONLY)))
|
||||
{
|
||||
Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: illegal parameter\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::CL_SelectStringTableEntryInDvar_f();
|
||||
}
|
||||
|
||||
__declspec(naked) int Security::G_GetClientScore()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax, [esp + 4] // index
|
||||
mov ecx, ds:1A831A8h // level: &g_clients
|
||||
|
||||
test ecx, ecx
|
||||
jz invalid_ptr
|
||||
|
||||
imul eax, 366Ch
|
||||
mov eax, [eax + ecx + 3134h]
|
||||
ret
|
||||
|
||||
invalid_ptr:
|
||||
xor eax, eax
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
void Security::G_LogPrintfStub(const char* fmt)
|
||||
{
|
||||
Game::G_LogPrintf("%s", fmt);
|
||||
}
|
||||
|
||||
Security::Security()
|
||||
{
|
||||
// Exploit fixes
|
||||
Utils::Hook(0x414D92, MsgReadBitsCompressCheckSV, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommands
|
||||
Utils::Hook(0x4A9F56, MsgReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
|
||||
Utils::Hook(0x407376, SVCanReplaceServerCommand, HOOK_CALL).install()->quick(); // SV_CanReplaceServerCommand
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x412370, 0xC3); // SV_SteamAuthClient
|
||||
Utils::Hook::Set<BYTE>(0x5A8C70, 0xC3); // CL_HandleRelayPacket
|
||||
|
||||
Utils::Hook::Nop(0x41698E, 5); // Disable Svcmd_EntityList_f
|
||||
|
||||
// Patch selectStringTableEntryInDvar
|
||||
Utils::Hook::Set<void(*)()>(0x405959, Security::SelectStringTableEntryInDvarStub);
|
||||
|
||||
// Patch G_GetClientScore for uninitialized game
|
||||
Utils::Hook(0x469AC0, G_GetClientScore, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Requests can be malicious
|
||||
Utils::Hook(0x5B67ED, AtolAdjustPlayerLimit, HOOK_CALL).install()->quick(); // PartyHost_HandleJoinPartyRequest
|
||||
|
||||
// Patch unsecure call to G_LogPrint inside GScr_LogPrint
|
||||
// This function is unsafe because IW devs forgot to G_LogPrintf("%s", fmt)
|
||||
Utils::Hook(0x5F70B5, G_LogPrintfStub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
}
|
24
src/Components/Modules/Security.hpp
Normal file
24
src/Components/Modules/Security.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Security : public Component
|
||||
{
|
||||
public:
|
||||
Security();
|
||||
|
||||
private:
|
||||
static int MsgReadBitsCompressCheckSV(const char* from, char* to, int size);
|
||||
static int MsgReadBitsCompressCheckCL(const char* from, char* to, int size);
|
||||
|
||||
static int SVCanReplaceServerCommand(Game::client_t* client, const char* cmd);
|
||||
|
||||
static long AtolAdjustPlayerLimit(const char* string);
|
||||
|
||||
static void SelectStringTableEntryInDvarStub();
|
||||
|
||||
static int G_GetClientScore();
|
||||
|
||||
static void G_LogPrintfStub(const char* fmt);
|
||||
};
|
||||
}
|
@ -2,24 +2,24 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<std::int32_t, Utils::Slot<bool(Command::Params*)>> ServerCommands::Commands;
|
||||
std::unordered_map<std::int32_t, std::function<bool(Command::Params*)>> ServerCommands::Commands;
|
||||
|
||||
void ServerCommands::OnCommand(std::int32_t cmd, Utils::Slot<bool(Command::Params*)> cb)
|
||||
void ServerCommands::OnCommand(std::int32_t cmd, std::function<bool(Command::Params*)> callback)
|
||||
{
|
||||
ServerCommands::Commands[cmd] = cb;
|
||||
ServerCommands::Commands.insert_or_assign(cmd, std::move(callback));
|
||||
}
|
||||
|
||||
bool ServerCommands::OnServerCommand()
|
||||
{
|
||||
Command::ClientParams params;
|
||||
|
||||
for (const auto& serverCommandCB : ServerCommands::Commands)
|
||||
for (const auto& [id, callback] : ServerCommands::Commands)
|
||||
{
|
||||
if (params.size() >= 1)
|
||||
{
|
||||
if (params.get(0)[0] == serverCommandCB.first)
|
||||
if (params.get(0)[0] == id) // Compare ID of server command
|
||||
{
|
||||
return serverCommandCB.second(¶ms);
|
||||
return callback(¶ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,7 @@ namespace Components
|
||||
return false;
|
||||
}
|
||||
|
||||
__declspec(naked) void ServerCommands::OnServerCommandStub()
|
||||
__declspec(naked) void ServerCommands::CG_DeployServerCommand_Stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
@ -44,7 +44,7 @@ namespace Components
|
||||
test eax, eax
|
||||
jle error
|
||||
|
||||
mov eax, DWORD PTR[edx * 4 + 1AAC634h]
|
||||
mov eax, dword ptr [edx * 4 + 1AAC634h]
|
||||
mov eax, [eax]
|
||||
|
||||
push 5944B3h
|
||||
@ -63,6 +63,6 @@ namespace Components
|
||||
ServerCommands::ServerCommands()
|
||||
{
|
||||
// Server command receive hook
|
||||
Utils::Hook(0x59449F, ServerCommands::OnServerCommandStub).install()->quick();
|
||||
Utils::Hook(0x59449F, ServerCommands::CG_DeployServerCommand_Stub).install()->quick();
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ namespace Components
|
||||
public:
|
||||
ServerCommands();
|
||||
|
||||
static void OnCommand(std::int32_t cmd, Utils::Slot<bool(Command::Params*)> cb);
|
||||
static void OnCommand(std::int32_t cmd, std::function<bool(Command::Params*)> callback);
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::int32_t, Utils::Slot<bool(Command::Params*)>> Commands;
|
||||
static std::unordered_map<std::int32_t, std::function<bool(Command::Params*)>> Commands;
|
||||
|
||||
static bool OnServerCommand();
|
||||
static void OnServerCommandStub();
|
||||
static void CG_DeployServerCommand_Stub();
|
||||
};
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ namespace Components
|
||||
{
|
||||
ServerInfo::Container ServerInfo::PlayerContainer;
|
||||
|
||||
Game::dvar_t** ServerInfo::CGScoreboardHeight;
|
||||
Game::dvar_t** ServerInfo::CGScoreboardWidth;
|
||||
|
||||
unsigned int ServerInfo::GetPlayerCount()
|
||||
{
|
||||
return ServerInfo::PlayerContainer.playerList.size();
|
||||
@ -74,22 +77,24 @@ namespace Components
|
||||
void ServerInfo::DrawScoreboardInfo(int localClientNum)
|
||||
{
|
||||
Game::Font_s* font = Game::R_RegisterFont("fonts/bigfont", 0);
|
||||
void* cxt = Game::ScrPlace_GetActivePlacement(localClientNum);
|
||||
const auto* cxt = Game::ScrPlace_GetActivePlacement(localClientNum);
|
||||
|
||||
std::string addressText = Network::Address(*Game::connectedHost).getString();
|
||||
if (addressText == "0.0.0.0:0" || addressText == "loopback") addressText = "Listen Server";
|
||||
auto addressText = Network::Address(*Game::connectedHost).getString();
|
||||
|
||||
// get x positions
|
||||
float fontSize = 0.35f;
|
||||
float y = (480.0f - Dvar::Var("cg_scoreboardHeight").get<float>()) * 0.5f;
|
||||
y += Dvar::Var("cg_scoreboardHeight").get<float>() + 6.0f;
|
||||
if (addressText == "0.0.0.0:0" || addressText == "loopback")
|
||||
addressText = "Listen Server";
|
||||
|
||||
float x = 320.0f - Dvar::Var("cg_scoreboardWidth").get<float>() * 0.5f;
|
||||
float x2 = 320.0f + Dvar::Var("cg_scoreboardWidth").get<float>() * 0.5f;
|
||||
// Get x positions
|
||||
auto y = (480.0f - (*ServerInfo::CGScoreboardHeight)->current.value) * 0.5f;
|
||||
y += (*ServerInfo::CGScoreboardHeight)->current.value + 6.0f;
|
||||
|
||||
// draw only when stream friendly ui is not enabled
|
||||
if (!Dvar::Var("ui_streamFriendly").get<bool>())
|
||||
const auto x = 320.0f - (*ServerInfo::CGScoreboardWidth)->current.value * 0.5f;
|
||||
const auto x2 = 320.0f + (*ServerInfo::CGScoreboardWidth)->current.value * 0.5f;
|
||||
|
||||
// Draw only when stream friendly ui is not enabled
|
||||
if (!Friends::UIStreamFriendly.get<bool>())
|
||||
{
|
||||
constexpr auto fontSize = 0.35f;
|
||||
Game::UI_DrawText(cxt, reinterpret_cast<const char*>(0x7ED3F8), 0x7FFFFFFF, font, x, y, 0, 0, fontSize, reinterpret_cast<float*>(0x747F34), 3);
|
||||
Game::UI_DrawText(cxt, addressText.data(), 0x7FFFFFFF, font, x2 - Game::UI_TextWidth(addressText.data(), 0, font, fontSize), y, 0, 0, fontSize, reinterpret_cast<float*>(0x747F34), 3);
|
||||
}
|
||||
@ -125,7 +130,7 @@ namespace Components
|
||||
|
||||
Utils::InfoString ServerInfo::GetInfo()
|
||||
{
|
||||
int maxclientCount = *Game::svs_numclients;
|
||||
int maxclientCount = *Game::svs_clientCount;
|
||||
|
||||
if (!maxclientCount)
|
||||
{
|
||||
@ -172,7 +177,9 @@ namespace Components
|
||||
ServerInfo::ServerInfo()
|
||||
{
|
||||
ServerInfo::PlayerContainer.currentPlayer = 0;
|
||||
ServerInfo::PlayerContainer.playerList.clear();
|
||||
|
||||
ServerInfo::CGScoreboardHeight = reinterpret_cast<Game::dvar_t**>(0x9FD070);
|
||||
ServerInfo::CGScoreboardWidth = reinterpret_cast<Game::dvar_t**>(0x9FD0AC);
|
||||
|
||||
// Draw IP and hostname on the scoreboard
|
||||
Utils::Hook(0x4FC6EA, ServerInfo::DrawScoreboardStub, HOOK_CALL).install()->quick();
|
||||
|
@ -28,6 +28,9 @@ namespace Components
|
||||
Network::Address target;
|
||||
};
|
||||
|
||||
static Game::dvar_t** CGScoreboardHeight;
|
||||
static Game::dvar_t** CGScoreboardWidth;
|
||||
|
||||
static Container PlayerContainer;
|
||||
|
||||
static void ServerStatus(UIScript::Token);
|
||||
|
@ -14,6 +14,13 @@ namespace Components
|
||||
|
||||
std::vector<unsigned int> ServerList::VisibleList;
|
||||
|
||||
Dvar::Var ServerList::UIServerSelected;
|
||||
Dvar::Var ServerList::UIServerSelectedMap;
|
||||
Dvar::Var ServerList::NETServerQueryLimit;
|
||||
Dvar::Var ServerList::NETServerFrames;
|
||||
|
||||
bool ServerList::useMasterServer = true;
|
||||
|
||||
std::vector<ServerList::ServerInfo>* ServerList::GetList()
|
||||
{
|
||||
if (ServerList::IsOnlineList())
|
||||
@ -154,13 +161,13 @@ namespace Components
|
||||
|
||||
if (info)
|
||||
{
|
||||
Dvar::Var("ui_serverSelected").set(true);
|
||||
Dvar::Var("ui_serverSelectedMap").set(info->mapname);
|
||||
ServerList::UIServerSelected.set(true);
|
||||
ServerList::UIServerSelectedMap.set(info->mapname);
|
||||
Dvar::Var("ui_serverSelectedGametype").set(info->gametype);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dvar::Var("ui_serverSelected").set(false);
|
||||
ServerList::UIServerSelected.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,13 +207,6 @@ namespace Components
|
||||
auto list = ServerList::GetList();
|
||||
if (!list) return;
|
||||
|
||||
// Refresh entirely, if there is no entry in the list
|
||||
if (list->empty())
|
||||
{
|
||||
ServerList::Refresh(UIScript::Token());
|
||||
return;
|
||||
}
|
||||
|
||||
bool ui_browserShowFull = Dvar::Var("ui_browserShowFull").get<bool>();
|
||||
bool ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get<bool>();
|
||||
int ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get<int>();
|
||||
@ -269,22 +269,28 @@ namespace Components
|
||||
}
|
||||
else if (ServerList::IsOnlineList())
|
||||
{
|
||||
#ifdef USE_LEGACY_SERVER_LIST
|
||||
const auto masterPort = Dvar::Var("masterPort").get<int>();
|
||||
const auto masterServerName = Dvar::Var("masterServerName").get<const char*>();
|
||||
|
||||
// Check if our dvars can properly convert to a address
|
||||
Game::netadr_t masterServerAddr;
|
||||
if (!ServerList::GetMasterServer(masterServerName, masterPort, masterServerAddr))
|
||||
{
|
||||
Logger::Print("Could not resolve address for %s:%u", masterServerName, masterPort);
|
||||
Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%u", masterServerName, masterPort), 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000);
|
||||
|
||||
useMasterServer = true;
|
||||
|
||||
ServerList::RefreshContainer.awatingList = true;
|
||||
ServerList::RefreshContainer.awaitTime = Game::Sys_Milliseconds();
|
||||
|
||||
int masterPort = Dvar::Var("masterPort").get<int>();
|
||||
const char* masterServerName = Dvar::Var("masterServerName").get<const char*>();
|
||||
|
||||
ServerList::RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort));
|
||||
|
||||
Logger::Print("Sending serverlist request to master: %s:%u\n", masterServerName, masterPort);
|
||||
|
||||
Logger::Print("Sending serverlist request to master\n");
|
||||
Network::SendCommand(ServerList::RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL));
|
||||
//Network::SendCommand(ServerList::RefreshContainer.Host, "getservers", "0 full empty");
|
||||
#else
|
||||
Node::Synchronize();
|
||||
#endif
|
||||
}
|
||||
else if (ServerList::IsFavouriteList())
|
||||
{
|
||||
@ -565,8 +571,7 @@ namespace Components
|
||||
void ServerList::SortList()
|
||||
{
|
||||
// Only sort when the serverlist is open
|
||||
Game::menuDef_t* menu = Game::Menus_FindByName(Game::uiContext, "pc_join_unranked");
|
||||
if (!menu || !Game::Menu_IsVisible(Game::uiContext, menu)) return;
|
||||
if (!ServerList::IsServerListOpen()) return;
|
||||
|
||||
std::stable_sort(ServerList::VisibleList.begin(), ServerList::VisibleList.end(), [](const unsigned int &server1, const unsigned int &server2) -> bool
|
||||
{
|
||||
@ -621,24 +626,37 @@ namespace Components
|
||||
void ServerList::Frame()
|
||||
{
|
||||
static Utils::Time::Interval frameLimit;
|
||||
int interval = static_cast<int>(1000.0f / Dvar::Var("net_serverFrames").get<int>());
|
||||
if (!frameLimit.elapsed(std::chrono::milliseconds(interval))) return;
|
||||
const auto interval = static_cast<int>(1000.0f / ServerList::NETServerFrames.get<int>());
|
||||
|
||||
if (!frameLimit.elapsed(std::chrono::milliseconds(interval)))
|
||||
return;
|
||||
|
||||
frameLimit.update();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
|
||||
|
||||
if (ServerList::RefreshContainer.awatingList)
|
||||
{
|
||||
// Check if we haven't got a response within 10 seconds
|
||||
// Stop counting if we are out of the server browser menu
|
||||
if (!ServerList::IsServerListOpen())
|
||||
{
|
||||
ServerList::RefreshContainer.awatingList = false;
|
||||
}
|
||||
|
||||
// Check if we haven't got a response within 5 seconds
|
||||
if (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime > 5000)
|
||||
{
|
||||
ServerList::RefreshContainer.awatingList = false;
|
||||
|
||||
Logger::Print("We haven't received a response from the master within %d seconds!\n", (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime) / 1000);
|
||||
Toast::Show("cardicon_headshot", "^1Error", "Failed to reach master server, using node servers instead.", 5000);
|
||||
|
||||
useMasterServer = false;
|
||||
Node::Synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
int requestLimit = Dvar::Var("net_serverQueryLimit").get<int>();
|
||||
auto requestLimit = ServerList::NETServerQueryLimit.get<int>();
|
||||
for (unsigned int i = 0; i < ServerList::RefreshContainer.servers.size() && requestLimit > 0; ++i)
|
||||
{
|
||||
ServerList::Container::ServerContainer* server = &ServerList::RefreshContainer.servers[i];
|
||||
@ -725,6 +743,20 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerList::GetMasterServer(const char* ip, int port, Game::netadr_t& address)
|
||||
{
|
||||
return Game::NET_StringToAdr(Utils::String::VA("%s:%u", ip, port), &address);
|
||||
}
|
||||
|
||||
bool ServerList::IsServerListOpen()
|
||||
{
|
||||
auto* menu = Game::Menus_FindByName(Game::uiContext, "pc_join_unranked");
|
||||
if (!menu)
|
||||
return false;
|
||||
|
||||
return Game::Menu_IsVisible(Game::uiContext, menu);
|
||||
}
|
||||
|
||||
ServerList::ServerList()
|
||||
{
|
||||
ServerList::OnlineList.clear();
|
||||
@ -734,11 +766,15 @@ namespace Components
|
||||
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
Dvar::Register<bool>("ui_serverSelected", false, Game::dvar_flag::DVAR_NONE, "Whether a server has been selected in the serverlist");
|
||||
Dvar::Register<const char*>("ui_serverSelectedMap", "mp_afghan", Game::dvar_flag::DVAR_NONE, "Map of the selected server");
|
||||
ServerList::UIServerSelected = Dvar::Register<bool>("ui_serverSelected", false,
|
||||
Game::dvar_flag::DVAR_NONE, "Whether a server has been selected in the serverlist");
|
||||
ServerList::UIServerSelectedMap = Dvar::Register<const char*>("ui_serverSelectedMap", "mp_afghan",
|
||||
Game::dvar_flag::DVAR_NONE, "Map of the selected server");
|
||||
|
||||
Dvar::Register<int>("net_serverQueryLimit", 1, 1, 10, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server queries per frame");
|
||||
Dvar::Register<int>("net_serverFrames", 30, 1, 60, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server query frames per second");
|
||||
ServerList::NETServerQueryLimit = Dvar::Register<int>("net_serverQueryLimit", 1,
|
||||
1, 10, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server queries per frame");
|
||||
ServerList::NETServerFrames = Dvar::Register<int>("net_serverFrames", 30,
|
||||
1, 60, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server query frames per second");
|
||||
});
|
||||
|
||||
// Fix ui_netsource dvar
|
||||
@ -780,11 +816,9 @@ namespace Components
|
||||
});
|
||||
|
||||
// Set default masterServerName + port and save it
|
||||
#ifdef USE_LEGACY_SERVER_LIST
|
||||
Utils::Hook::Set<char*>(0x60AD92, "127.0.0.1");
|
||||
Utils::Hook::Set<const char*>(0x60AD92, "master.xlabs.dev");
|
||||
Utils::Hook::Set<BYTE>(0x60AD90, Game::dvar_flag::DVAR_ARCHIVE); // masterServerName
|
||||
Utils::Hook::Set<BYTE>(0x60ADC6, Game::dvar_flag::DVAR_ARCHIVE); // masterPort
|
||||
#endif
|
||||
|
||||
// Add server list feeder
|
||||
UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer);
|
||||
@ -794,6 +828,7 @@ namespace Components
|
||||
UIScript::Add("RefreshFilter", ServerList::UpdateVisibleList);
|
||||
|
||||
UIScript::Add("RefreshServers", ServerList::Refresh);
|
||||
|
||||
UIScript::Add("JoinServer", [](UIScript::Token)
|
||||
{
|
||||
ServerList::ServerInfo* info = ServerList::GetServer(ServerList::CurrentServer);
|
||||
@ -803,6 +838,7 @@ namespace Components
|
||||
Party::Connect(info->addr);
|
||||
}
|
||||
});
|
||||
|
||||
UIScript::Add("ServerSort", [](UIScript::Token token)
|
||||
{
|
||||
int key = token.get<int>();
|
||||
@ -820,6 +856,7 @@ namespace Components
|
||||
Logger::Print("Sorting server list by token: %d\n", ServerList::SortKey);
|
||||
ServerList::SortList();
|
||||
});
|
||||
|
||||
UIScript::Add("CreateListFavorite", [](UIScript::Token)
|
||||
{
|
||||
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
|
||||
@ -829,10 +866,12 @@ namespace Components
|
||||
ServerList::StoreFavourite(info->addr.getString());
|
||||
}
|
||||
});
|
||||
|
||||
UIScript::Add("CreateFavorite", [](UIScript::Token)
|
||||
{
|
||||
ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get<std::string>());
|
||||
});
|
||||
|
||||
UIScript::Add("CreateCurrentServerFavorite", [](UIScript::Token)
|
||||
{
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
@ -844,6 +883,7 @@ namespace Components
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
UIScript::Add("DeleteFavorite", [](UIScript::Token)
|
||||
{
|
||||
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
|
||||
@ -854,28 +894,24 @@ namespace Components
|
||||
};
|
||||
});
|
||||
|
||||
#ifdef _DEBUG
|
||||
Command::Add("playerCount", [](Command::Params*)
|
||||
{
|
||||
int count = 0;
|
||||
for (auto server : ServerList::OnlineList)
|
||||
auto count = 0;
|
||||
for (const auto& server : ServerList::OnlineList)
|
||||
{
|
||||
count += server.clients;
|
||||
}
|
||||
|
||||
Logger::Print("There are %d players playing.\n", count);
|
||||
});
|
||||
|
||||
#endif
|
||||
// Add required ownerDraws
|
||||
UIScript::AddOwnerDraw(220, ServerList::UpdateSource);
|
||||
UIScript::AddOwnerDraw(253, ServerList::UpdateGameType);
|
||||
|
||||
// Add frame callback
|
||||
Scheduler::OnFrame(ServerList::Frame);
|
||||
|
||||
// This is placed here in case the anticheat has been disabled!
|
||||
#if !defined(DISABLE_ANTICHEAT) && defined(PROCTECT_PROCESS)
|
||||
Scheduler::OnFrame(AntiCheat::ReadIntegrityCheck, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
ServerList::~ServerList()
|
||||
|
@ -50,6 +50,9 @@ namespace Components
|
||||
|
||||
static void UpdateVisibleInfo();
|
||||
|
||||
static bool GetMasterServer(const char* ip, int port, Game::netadr_t& address);
|
||||
static bool useMasterServer;
|
||||
|
||||
private:
|
||||
enum Column
|
||||
{
|
||||
@ -138,5 +141,12 @@ namespace Components
|
||||
static std::vector<ServerInfo> FavouriteList;
|
||||
|
||||
static std::vector<unsigned int> VisibleList;
|
||||
|
||||
static Dvar::Var UIServerSelected;
|
||||
static Dvar::Var UIServerSelectedMap;
|
||||
static Dvar::Var NETServerQueryLimit;
|
||||
static Dvar::Var NETServerFrames;
|
||||
|
||||
static bool IsServerListOpen();
|
||||
};
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ namespace Components
|
||||
SlowMotion::Delay = delay;
|
||||
|
||||
// set snapshot num to 1 behind (T6 does this, why shouldn't we?)
|
||||
for (int i = 0; i < *Game::svs_numclients; ++i)
|
||||
for (int i = 0; i < *Game::svs_clientCount; ++i)
|
||||
{
|
||||
Game::svs_clients[i].snapNum = *reinterpret_cast<DWORD*>(0x31D9384) - 1;
|
||||
}
|
||||
|
@ -1356,11 +1356,11 @@ namespace Components
|
||||
if (*in) // height
|
||||
in++;
|
||||
|
||||
if(*in) // material name length + material name characters
|
||||
if (*in) // material name length + material name characters
|
||||
{
|
||||
const auto materialNameLength = *in;
|
||||
in++;
|
||||
for(auto i = 0; i < materialNameLength; i++)
|
||||
for (auto i = 0; i < materialNameLength; i++)
|
||||
{
|
||||
if (*in)
|
||||
in++;
|
||||
@ -1370,7 +1370,7 @@ namespace Components
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*in == FONT_ICON_SEPARATOR_CHARACTER)
|
||||
if (*in == FONT_ICON_SEPARATOR_CHARACTER)
|
||||
{
|
||||
const auto* fontIconEndPos = &in[1];
|
||||
FontIconInfo fontIcon{};
|
||||
@ -1386,6 +1386,7 @@ namespace Components
|
||||
++current;
|
||||
++in;
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,17 @@ namespace Components
|
||||
if (params.size() <= 1)
|
||||
return 0;
|
||||
|
||||
int index = atoi(params[1]);
|
||||
char* end;
|
||||
const auto* input = params.get(1);
|
||||
auto index = std::strtol(input, &end, 10);
|
||||
|
||||
if (input == end)
|
||||
{
|
||||
Logger::Print("Warning: %s is not a valid input\n"
|
||||
"Usage: %s <weapon index>\n",
|
||||
input, params.get(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (index >= 4139)
|
||||
{
|
||||
@ -56,7 +66,7 @@ namespace Components
|
||||
return 0;
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void(int, int)>(0x4BD520)(0, index);
|
||||
Game::CG_SetupWeaponDef(0, index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ namespace Components
|
||||
|
||||
HWND Window::MainWindow = nullptr;
|
||||
BOOL Window::CursorVisible = TRUE;
|
||||
std::unordered_map<UINT, Utils::Slot<Window::WndProcCallback>> Window::WndMessageCallbacks;
|
||||
Utils::Signal<Window::CreateCallback> Window::CreateSignals;
|
||||
|
||||
int Window::Width()
|
||||
{
|
||||
@ -66,6 +68,16 @@ namespace Components
|
||||
return Window::MainWindow;
|
||||
}
|
||||
|
||||
void Window::OnWndMessage(UINT Msg, Utils::Slot<Window::WndProcCallback> callback)
|
||||
{
|
||||
WndMessageCallbacks.emplace(Msg, callback);
|
||||
}
|
||||
|
||||
void Window::OnCreate(Utils::Slot<CreateCallback> callback)
|
||||
{
|
||||
CreateSignals.connect(callback);
|
||||
}
|
||||
|
||||
int Window::IsNoBorder()
|
||||
{
|
||||
return Window::NoBorder.get<bool>();
|
||||
@ -121,6 +133,9 @@ namespace Components
|
||||
HWND WINAPI Window::CreateMainWindow(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
|
||||
{
|
||||
Window::MainWindow = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
||||
|
||||
CreateSignals();
|
||||
|
||||
return Window::MainWindow;
|
||||
}
|
||||
|
||||
@ -132,15 +147,21 @@ namespace Components
|
||||
|
||||
BOOL WINAPI Window::MessageHandler(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (Msg == WM_SETCURSOR)
|
||||
if (const auto cb = WndMessageCallbacks.find(Msg); cb != WndMessageCallbacks.end())
|
||||
{
|
||||
Window::ApplyCursor();
|
||||
return TRUE;
|
||||
return cb->second(lParam, wParam);
|
||||
}
|
||||
|
||||
return Utils::Hook::Call<BOOL(__stdcall)(HWND, UINT, WPARAM, LPARAM)>(0x4731F0)(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
|
||||
void Window::EnableDpiAwareness()
|
||||
{
|
||||
const Utils::Library user32{"user32.dll"};
|
||||
|
||||
user32.invokePascal<void>("SetProcessDpiAwarenessContext", DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
|
||||
Window::Window()
|
||||
{
|
||||
// Borderless window
|
||||
@ -184,5 +205,13 @@ namespace Components
|
||||
|
||||
// Use custom message handler
|
||||
Utils::Hook::Set(0x64D298, Window::MessageHandler);
|
||||
|
||||
Window::OnWndMessage(WM_SETCURSOR, [](WPARAM, LPARAM)
|
||||
{
|
||||
Window::ApplyCursor();
|
||||
return TRUE;
|
||||
});
|
||||
|
||||
Window::EnableDpiAwareness();
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ namespace Components
|
||||
class Window : public Component
|
||||
{
|
||||
public:
|
||||
typedef BOOL(WndProcCallback)(WPARAM wParam, LPARAM lParam);
|
||||
typedef void(CreateCallback)();
|
||||
|
||||
Window();
|
||||
|
||||
static int Width();
|
||||
@ -18,10 +21,15 @@ namespace Components
|
||||
|
||||
static HWND GetWindow();
|
||||
|
||||
static void OnWndMessage(UINT Msg, Utils::Slot<WndProcCallback> callback);
|
||||
|
||||
static void OnCreate(Utils::Slot<CreateCallback> callback);
|
||||
private:
|
||||
static BOOL CursorVisible;
|
||||
static Dvar::Var NoBorder;
|
||||
static Dvar::Var NativeCursor;
|
||||
static std::unordered_map<UINT, Utils::Slot<WndProcCallback>> WndMessageCallbacks;
|
||||
static Utils::Signal<CreateCallback> CreateSignals;
|
||||
|
||||
static HWND MainWindow;
|
||||
|
||||
@ -36,5 +44,7 @@ namespace Components
|
||||
|
||||
static void StyleHookStub();
|
||||
static HWND WINAPI CreateMainWindow(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
|
||||
|
||||
static void EnableDpiAwareness();
|
||||
};
|
||||
}
|
||||
|
@ -5,19 +5,19 @@ namespace Game
|
||||
std::vector<std::string> Sys_ListFilesWrapper(const std::string& directory, const std::string& extension)
|
||||
{
|
||||
auto fileCount = 0;
|
||||
auto files = Game::Sys_ListFiles(directory.data(), extension.data(), 0, &fileCount, 0);
|
||||
auto** const files = Sys_ListFiles(directory.data(), extension.data(), nullptr, &fileCount, 0);
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (auto i = 0; i < fileCount; i++)
|
||||
{
|
||||
if (files[i])
|
||||
if (files[i] != nullptr)
|
||||
{
|
||||
result.push_back(files[i]);
|
||||
result.emplace_back(files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Game::FS_FreeFileList(files);
|
||||
FS_FreeFileList(files);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -31,6 +31,7 @@ namespace Game
|
||||
BG_GetWeaponName_t BG_GetWeaponName = BG_GetWeaponName_t(0x4E6EC0);
|
||||
BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj = BG_LoadWeaponDef_LoadObj_t(0x57B5F0);
|
||||
BG_GetWeaponDef_t BG_GetWeaponDef = BG_GetWeaponDef_t(0x440EB0);
|
||||
BG_GetEntityTypeName_t BG_GetEntityTypeName = BG_GetEntityTypeName_t(0x43A0E0);
|
||||
|
||||
Cbuf_AddServerText_t Cbuf_AddServerText = Cbuf_AddServerText_t(0x4BB9B0);
|
||||
Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20);
|
||||
@ -44,6 +45,8 @@ namespace Game
|
||||
CG_ScoresUp_f_t CG_ScoresUp_f = CG_ScoresUp_f_t(0x5802C0);
|
||||
CG_ScrollScoreboardUp_t CG_ScrollScoreboardUp = CG_ScrollScoreboardUp_t(0x47A5C0);
|
||||
CG_ScrollScoreboardDown_t CG_ScrollScoreboardDown = CG_ScrollScoreboardDown_t(0x493B50);
|
||||
CG_GetTeamName_t CG_GetTeamName = CG_GetTeamName_t(0x4B6210);
|
||||
CG_SetupWeaponDef_t CG_SetupWeaponDef = CG_SetupWeaponDef_t(0x4BD520);
|
||||
|
||||
CL_GetClientName_t CL_GetClientName = CL_GetClientName_t(0x4563D0);
|
||||
CL_IsCgameInitialized_t CL_IsCgameInitialized = CL_IsCgameInitialized_t(0x43EB20);
|
||||
@ -73,6 +76,7 @@ namespace Game
|
||||
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
|
||||
Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20);
|
||||
Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000);
|
||||
Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200);
|
||||
|
||||
Con_DrawMiniConsole_t Con_DrawMiniConsole = Con_DrawMiniConsole_t(0x464F30);
|
||||
Con_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040);
|
||||
@ -149,8 +153,13 @@ namespace Game
|
||||
FS_IsShippedIWD_t FS_IsShippedIWD = FS_IsShippedIWD_t(0x642440);
|
||||
FS_Delete_t FS_Delete = FS_Delete_t(0x48A5B0);
|
||||
|
||||
G_LogPrintf_t G_LogPrintf = G_LogPrintf_t(0x4B0150);
|
||||
G_GetWeaponIndexForName_t G_GetWeaponIndexForName = G_GetWeaponIndexForName_t(0x49E540);
|
||||
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
||||
G_PrintEntities_t G_PrintEntities = G_PrintEntities_t(0x4E6A50);
|
||||
G_GetEntityTypeName_t G_GetEntityTypeName = G_GetEntityTypeName_t(0x4EB810);
|
||||
|
||||
Svcmd_EntityList_f_t Svcmd_EntityList_f = Svcmd_EntityList_f_t(0x4B6A70);
|
||||
|
||||
GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = GScr_LoadGameTypeScript_t(0x4ED9A0);
|
||||
|
||||
@ -184,7 +193,7 @@ namespace Game
|
||||
Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray = Load_snd_alias_list_nameArray_t(0x4499F0);
|
||||
|
||||
Menus_CloseAll_t Menus_CloseAll = Menus_CloseAll_t(0x4BA5B0);
|
||||
Menus_CloseRequest_t Menus_CloseRequest = Menus_CloseRequest_t(0x430D50);
|
||||
Menus_CloseRequest_t Menus_CloseRequest = Menus_CloseRequest_t(0x430D50);
|
||||
Menus_OpenByName_t Menus_OpenByName = Menus_OpenByName_t(0x4CCE60);
|
||||
Menus_FindByName_t Menus_FindByName = Menus_FindByName_t(0x487240);
|
||||
Menu_IsVisible_t Menu_IsVisible = Menu_IsVisible_t(0x4D77D0);
|
||||
@ -259,6 +268,8 @@ namespace Game
|
||||
Scr_GetFunctionHandle_t Scr_GetFunctionHandle = Scr_GetFunctionHandle_t(0x4234F0);
|
||||
|
||||
Scr_GetString_t Scr_GetString = Scr_GetString_t(0x425900);
|
||||
Scr_GetConstString_t Scr_GetConstString = Scr_GetConstString_t(0x494830);
|
||||
Scr_GetDebugString_t Scr_GetDebugString = Scr_GetDebugString_t(0x4EBF50);
|
||||
Scr_GetFloat_t Scr_GetFloat = Scr_GetFloat_t(0x443140);
|
||||
Scr_GetInt_t Scr_GetInt = Scr_GetInt_t(0x4F31D0);
|
||||
Scr_GetObject_t Scr_GetObject = Scr_GetObject_t(0x462100);
|
||||
@ -269,16 +280,31 @@ namespace Game
|
||||
|
||||
Scr_AddEntity_t Scr_AddEntity = Scr_AddEntity_t(0x4BFB40);
|
||||
Scr_AddString_t Scr_AddString = Scr_AddString_t(0x412310);
|
||||
Scr_AddConstString_t Scr_AddConstString = Scr_AddConstString_t(0x488860);
|
||||
Scr_AddIString_t Scr_AddIString = Scr_AddIString_t(0x455F20);
|
||||
Scr_AddInt_t Scr_AddInt = Scr_AddInt_t(0x41D7D0);
|
||||
Scr_AddFloat_t Scr_AddFloat = Scr_AddFloat_t(0x61E860);
|
||||
Scr_AddObject_t Scr_AddObject = Scr_AddObject_t(0x430F40);
|
||||
Scr_Notify_t Scr_Notify = Scr_Notify_t(0x4A4750);
|
||||
Scr_NotifyLevel_t Scr_NotifyLevel = Scr_NotifyLevel_t(0x4D9C30);
|
||||
|
||||
Scr_Error_t Scr_Error = Scr_Error_t(0x61E8B0);
|
||||
Scr_ObjectError_t Scr_ObjectError = Scr_ObjectError_t(0x42EF40);
|
||||
Scr_ParamError_t Scr_ParamError = Scr_ParamError_t(0x4FBC70);
|
||||
|
||||
Scr_GetType_t Scr_GetType = Scr_GetType_t(0x422900);
|
||||
|
||||
Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0);
|
||||
|
||||
Scr_GetObjectField_t Scr_GetObjectField = Scr_GetObjectField_t(0x4FF3D0);
|
||||
Scr_SetObjectField_t Scr_SetObjectField = Scr_SetObjectField_t(0x4F20F0);
|
||||
Scr_GetEntityField_t Scr_GetEntityField = Scr_GetEntityField_t(0x4E8390);
|
||||
Scr_SetClientField_t Scr_SetClientField = Scr_SetClientField_t(0x4A6DF0);
|
||||
Scr_AddClassField_t Scr_AddClassField = Scr_AddClassField_t(0x4C0E70);
|
||||
|
||||
GetEntity_t GetEntity = GetEntity_t(0x4BC270);
|
||||
GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0);
|
||||
|
||||
Scr_RegisterFunction_t Scr_RegisterFunction = Scr_RegisterFunction_t(0x492D50);
|
||||
Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650);
|
||||
Scr_IsSystemActive_t Scr_IsSystemActive = Scr_IsSystemActive_t(0x4B24E0);
|
||||
@ -302,6 +328,8 @@ namespace Game
|
||||
|
||||
SL_ConvertToString_t SL_ConvertToString = SL_ConvertToString_t(0x4EC1D0);
|
||||
SL_GetString_t SL_GetString = SL_GetString_t(0x4CDC10);
|
||||
SL_AddRefToString_t SL_AddRefToString = SL_AddRefToString_t(0x4D9B00);
|
||||
SL_RemoveRefToString_t SL_RemoveRefToString = SL_RemoveRefToString_t(0x47CD70);
|
||||
|
||||
SND_Init_t SND_Init = SND_Init_t(0x46A630);
|
||||
SND_InitDriver_t SND_InitDriver = SND_InitDriver_t(0x4F5090);
|
||||
@ -311,9 +339,11 @@ namespace Game
|
||||
Steam_JoinLobby_t Steam_JoinLobby = Steam_JoinLobby_t(0x49CF70);
|
||||
|
||||
StringTable_Lookup_t StringTable_Lookup = StringTable_Lookup_t(0x42F0E0);
|
||||
StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow = StringTable_GetColumnValueForRow_t(0x4F2C80);
|
||||
StringTable_HashString_t StringTable_HashString = StringTable_HashString_t(0x475EB0);
|
||||
|
||||
SV_AddTestClient_t SV_AddTestClient = SV_AddTestClient_t(0x48AD30);
|
||||
SV_IsTestClient_t SV_IsTestClient = SV_IsTestClient_t(0x4D6E40);
|
||||
SV_GameClientNum_Score_t SV_GameClientNum_Score = SV_GameClientNum_Score_t(0x469AC0);
|
||||
SV_GameSendServerCommand_t SV_GameSendServerCommand = SV_GameSendServerCommand_t(0x4BC3A0);
|
||||
SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString = SV_Cmd_TokenizeString_t(0x4B5780);
|
||||
@ -323,10 +353,10 @@ namespace Game
|
||||
SV_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0);
|
||||
SV_Loaded_t SV_Loaded = SV_Loaded_t(0x4EE3E0);
|
||||
SV_ClientThink_t SV_ClientThink = SV_ClientThink_t(0x44ADD0);
|
||||
SV_DropClient_t SV_DropClient = SV_DropClient_t(0x4D1600);
|
||||
SV_GetPlayerByName_t SV_GetPlayerByName = SV_GetPlayerByName_t(0x6242B0);
|
||||
SV_GetPlayerByNum_t SV_GetPlayerByNum = SV_GetPlayerByNum_t(0x624390);
|
||||
|
||||
Sys_Error_t Sys_Error = Sys_Error_t(0x4E0200);
|
||||
Sys_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580);
|
||||
Sys_IsDatabaseReady_t Sys_IsDatabaseReady = Sys_IsDatabaseReady_t(0x4CA4A0);
|
||||
Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2 = Sys_IsDatabaseReady2_t(0x441280);
|
||||
@ -339,6 +369,7 @@ namespace Game
|
||||
Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads = Sys_SuspendOtherThreads_t(0x45A190);
|
||||
Sys_ListFiles_t Sys_ListFiles = Sys_ListFiles_t(0x45A660);
|
||||
Sys_Milliseconds_t Sys_Milliseconds = Sys_Milliseconds_t(0x42A660);
|
||||
Sys_Error_t Sys_Error = Sys_Error_t(0x43D570);
|
||||
Sys_LockWrite_t Sys_LockWrite = Sys_LockWrite_t(0x435880);
|
||||
Sys_TempPriorityAtLeastNormalBegin_t Sys_TempPriorityAtLeastNormalBegin = Sys_TempPriorityAtLeastNormalBegin_t(0x478680);
|
||||
Sys_TempPriorityEnd_t Sys_TempPriorityEnd = Sys_TempPriorityEnd_t(0x4DCF00);
|
||||
@ -390,6 +421,13 @@ namespace Game
|
||||
PM_Trace_t PM_Trace = PM_Trace_t(0x441F60);
|
||||
PM_GetEffectiveStance_t PM_GetEffectiveStance = PM_GetEffectiveStance_t(0x412540);
|
||||
|
||||
CL_MouseEvent_t CL_MouseEvent = CL_MouseEvent_t(0x4D7C50);
|
||||
IN_RecenterMouse_t IN_RecenterMouse = IN_RecenterMouse_t(0x463D80);
|
||||
|
||||
IN_MouseMove_t IN_MouseMove = IN_MouseMove_t(0x64C490);
|
||||
IN_Init_t IN_Init = IN_Init_t(0x45D620);
|
||||
IN_Shutdown_t IN_Shutdown = IN_Shutdown_t(0x426360);
|
||||
|
||||
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
|
||||
unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8);
|
||||
|
||||
@ -405,10 +443,10 @@ namespace Game
|
||||
float* cgameFOVSensitivityScale = reinterpret_cast<float*>(0xB2F884);
|
||||
|
||||
int* svs_time = reinterpret_cast<int*>(0x31D9384);
|
||||
int* svs_numclients = reinterpret_cast<int*>(0x31D938C);
|
||||
int* svs_clientCount = reinterpret_cast<int*>(0x31D938C);
|
||||
client_t* svs_clients = reinterpret_cast<client_t*>(0x31D9390);
|
||||
|
||||
UiContext *uiContext = reinterpret_cast<UiContext *>(0x62E2858);
|
||||
UiContext* uiContext = reinterpret_cast<UiContext*>(0x62E2858);
|
||||
|
||||
int* arenaCount = reinterpret_cast<int*>(0x62E6930);
|
||||
mapArena_t* arenas = reinterpret_cast<mapArena_t*>(0x62E6934);
|
||||
@ -473,6 +511,7 @@ namespace Game
|
||||
unsigned short* db_hashTable = reinterpret_cast<unsigned short*>(0x12412B0);
|
||||
|
||||
scrVmPub_t* scrVmPub = reinterpret_cast<scrVmPub_t*>(0x2040CF0);
|
||||
scrVarPub_t* scrVarPub = reinterpret_cast<scrVarPub_t*>(0x201A408);
|
||||
|
||||
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
|
||||
|
||||
@ -501,10 +540,23 @@ namespace Game
|
||||
|
||||
GraphFloat* aaInputGraph = reinterpret_cast<GraphFloat*>(0x7A2FC0);
|
||||
|
||||
const char* MY_CMDS = reinterpret_cast<const char*>(0x73C9C4); // Count 5
|
||||
|
||||
XModel** cached_models = reinterpret_cast<XModel**>(0x1AA20C8);
|
||||
|
||||
FastCriticalSection* db_hashCritSect = reinterpret_cast<FastCriticalSection*>(0x16B8A54);
|
||||
|
||||
vec3_t* CorrectSolidDeltas = reinterpret_cast<vec3_t*>(0x739BB8); // Count 26
|
||||
|
||||
level_locals_t* level = reinterpret_cast<level_locals_t*>(0x1A831A8);
|
||||
|
||||
float(*penetrationDepthTable)[PENETRATE_TYPE_COUNT][SURF_TYPE_COUNT] = reinterpret_cast<float(*)[PENETRATE_TYPE_COUNT][SURF_TYPE_COUNT]>(0x7C4878);
|
||||
|
||||
WinMouseVars_t* s_wmv = reinterpret_cast<WinMouseVars_t*>(0x649D640);
|
||||
|
||||
int* window_center_x = reinterpret_cast<int*>(0x649D638);
|
||||
int* window_center_y = reinterpret_cast<int*>(0x649D630);
|
||||
|
||||
void Sys_LockRead(FastCriticalSection* critSect)
|
||||
{
|
||||
InterlockedIncrement(&critSect->readCount);
|
||||
@ -517,6 +569,13 @@ namespace Game
|
||||
InterlockedDecrement(&critSect->readCount);
|
||||
}
|
||||
|
||||
XModel* G_GetModel(const int index)
|
||||
{
|
||||
assert(index > 0);
|
||||
assert(index < MAX_MODELS);
|
||||
return cached_models[index];
|
||||
}
|
||||
|
||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
||||
{
|
||||
int elSize = DB_GetXAssetSizeHandlers[type]();
|
||||
@ -703,24 +762,49 @@ namespace Game
|
||||
return hash;
|
||||
}
|
||||
|
||||
void SV_KickClientError(client_t* client, const std::string& reason)
|
||||
void SV_GameDropClient(int clientNum, const char* reason)
|
||||
{
|
||||
if (client->state < 5)
|
||||
const auto maxClients = Dvar_FindVar("sv_maxclients")->current.integer;
|
||||
assert(maxClients >= 1 && maxClients <= 18);
|
||||
|
||||
if (clientNum >= 0 && clientNum < maxClients)
|
||||
{
|
||||
Components::Network::SendCommand(client->netchan.remoteAddress, "error", reason);
|
||||
SV_DropClient(&svs_clients[clientNum], reason, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SV_DropAllBots()
|
||||
{
|
||||
for (auto i = 0; i < *svs_clientCount; ++i)
|
||||
{
|
||||
if (svs_clients[i].state != clientstate_t::CS_FREE
|
||||
&& svs_clients[i].netchan.remoteAddress.type == netadrtype_t::NA_BOT)
|
||||
{
|
||||
SV_GameDropClient(i, "GAME_GET_TO_COVER");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IncInParam()
|
||||
{
|
||||
Scr_ClearOutParams();
|
||||
|
||||
if (scrVmPub->top == scrVmPub->maxStack)
|
||||
{
|
||||
Sys_Error("Internal script stack overflow");
|
||||
}
|
||||
|
||||
SV_KickClient(client, reason.data());
|
||||
scrVmPub->top++;
|
||||
scrVmPub->inparamcount++;
|
||||
}
|
||||
|
||||
void Scr_iPrintLn(int clientNum, const std::string& message)
|
||||
void Scr_AddBool(int value)
|
||||
{
|
||||
Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x66, message.data()));
|
||||
}
|
||||
assert(value == 0 || value == 1);
|
||||
|
||||
void Scr_iPrintLnBold(int clientNum, const std::string& message)
|
||||
{
|
||||
Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data()));
|
||||
IncInParam();
|
||||
scrVmPub->top->type = VAR_INTEGER;
|
||||
scrVmPub->top->u.intValue = value;
|
||||
}
|
||||
|
||||
int FS_FOpenFileReadCurrentThread(const char* file, int* fh)
|
||||
@ -1100,19 +1184,23 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
bool PM_IsAdsAllowed(Game::playerState_s* playerState)
|
||||
__declspec(naked) bool PM_IsAdsAllowed(playerState_s* /*ps*/)
|
||||
{
|
||||
bool result;
|
||||
|
||||
__asm
|
||||
{
|
||||
mov esi, playerState
|
||||
mov ebx, 0x5755A0
|
||||
call ebx
|
||||
mov result, al // AL
|
||||
}
|
||||
push eax
|
||||
pushad
|
||||
|
||||
return result;
|
||||
mov esi, [esp + 0x24 + 0x4] // ps
|
||||
mov ecx, 0x5755A0
|
||||
call ecx
|
||||
|
||||
mov [esp + 0x20], eax
|
||||
popad
|
||||
pop eax
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void FS_AddLocalizedGameDirectory(const char* /*path*/, const char* /*dir*/)
|
||||
@ -1164,28 +1252,6 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void SV_KickClient(client_t* /*client*/, const char* /*reason*/)
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
mov edi, 0
|
||||
mov esi, [esp + 24h]
|
||||
push [esp + 28h]
|
||||
push 0
|
||||
push 0
|
||||
|
||||
mov eax, 6249A0h
|
||||
call eax
|
||||
add esp, 0Ch
|
||||
|
||||
popad
|
||||
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Scr_NotifyId(unsigned int /*id*/, unsigned __int16 /*stringValue*/, unsigned int /*paramcount*/)
|
||||
{
|
||||
__asm
|
||||
@ -1207,6 +1273,27 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void RuntimeErrorInternal(int /*channel*/, const char* /*codePos*/, unsigned int /*index*/, const char* /*msg*/)
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
mov eax, [esp + 0x10 + 0x20] // msg
|
||||
mov edi, [esp + 0x4 + 0x20] // channel
|
||||
|
||||
push [esp + 0xC + 0x20] // index
|
||||
push [esp + 0xC + 0x20] // codePos
|
||||
|
||||
mov edx, 0x61ABE0
|
||||
call edx
|
||||
add esp, 0x8
|
||||
|
||||
popad
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void IN_KeyUp(kbutton_t* /*button*/)
|
||||
{
|
||||
__asm
|
||||
@ -1508,7 +1595,7 @@ namespace Game
|
||||
|
||||
__declspec(naked) void AimAssist_UpdateAdsLerp(const AimInput* /*aimInput*/)
|
||||
{
|
||||
__asm
|
||||
__asm
|
||||
{
|
||||
mov eax, [esp + 0x4]
|
||||
mov ebx, 0x569AA0
|
||||
@ -1540,5 +1627,20 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto SV_BotUserMove_Addr = 0x626E50;
|
||||
__declspec(naked) void SV_BotUserMove(client_t* /*client*/)
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
mov edi, [esp + 0x20 + 0x4]
|
||||
call SV_BotUserMove_Addr
|
||||
|
||||
popad
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#pragma optimize("", on)
|
||||
}
|
||||
|
@ -43,6 +43,9 @@ namespace Game
|
||||
typedef WeaponDef* (__cdecl * BG_GetWeaponDef_t)(int weaponIndex);
|
||||
extern BG_GetWeaponDef_t BG_GetWeaponDef;
|
||||
|
||||
typedef const char*(__cdecl * BG_GetEntityTypeName_t)(const int eType);
|
||||
extern BG_GetEntityTypeName_t BG_GetEntityTypeName;
|
||||
|
||||
typedef void(__cdecl * Cbuf_AddServerText_t)();
|
||||
extern Cbuf_AddServerText_t Cbuf_AddServerText;
|
||||
|
||||
@ -76,6 +79,12 @@ namespace Game
|
||||
typedef void(__cdecl * CG_ScrollScoreboardDown_t)(cg_s* cgameGlob);
|
||||
extern CG_ScrollScoreboardDown_t CG_ScrollScoreboardDown;
|
||||
|
||||
typedef const char*(__cdecl * CG_GetTeamName_t)(team_t team);
|
||||
extern CG_GetTeamName_t CG_GetTeamName;
|
||||
|
||||
typedef void(__cdecl * CG_SetupWeaponDef_t)(int localClientNum, unsigned int weapIndex);
|
||||
extern CG_SetupWeaponDef_t CG_SetupWeaponDef;
|
||||
|
||||
typedef char*(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, size_t size);
|
||||
extern CL_GetClientName_t CL_GetClientName;
|
||||
|
||||
@ -154,6 +163,9 @@ namespace Game
|
||||
typedef void(__cdecl * Com_Quitf_t)();
|
||||
extern Com_Quitf_t Com_Quit_f;
|
||||
|
||||
typedef void(__cdecl * Com_PrintWarning_t)(int, const char*, ...);
|
||||
extern Com_PrintWarning_t Com_PrintWarning;
|
||||
|
||||
typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha);
|
||||
extern Con_DrawMiniConsole_t Con_DrawMiniConsole;
|
||||
|
||||
@ -329,7 +341,7 @@ namespace Game
|
||||
typedef int(__cdecl * FS_FOpenFileReadForThread_t)(const char *filename, int *file, int thread);
|
||||
extern FS_FOpenFileReadForThread_t FS_FOpenFileReadForThread;
|
||||
|
||||
typedef int(__cdecl * FS_FCloseFile_t)(int fh);
|
||||
typedef int(__cdecl * FS_FCloseFile_t)(int stream);
|
||||
extern FS_FCloseFile_t FS_FCloseFile;
|
||||
|
||||
typedef bool(__cdecl * FS_FileExists_t)(const char* file);
|
||||
@ -368,12 +380,24 @@ namespace Game
|
||||
typedef int(__cdecl* FS_Delete_t)(const char* fileName);
|
||||
extern FS_Delete_t FS_Delete;
|
||||
|
||||
typedef int(__cdecl* G_GetWeaponIndexForName_t)(char*);
|
||||
typedef void(__cdecl * G_LogPrintf_t)(const char* fmt, ...);
|
||||
extern G_LogPrintf_t G_LogPrintf;
|
||||
|
||||
typedef unsigned int(__cdecl * G_GetWeaponIndexForName_t)(const char*);
|
||||
extern G_GetWeaponIndexForName_t G_GetWeaponIndexForName;
|
||||
|
||||
typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
|
||||
typedef void(__cdecl * G_SpawnEntitiesFromString_t)();
|
||||
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
||||
|
||||
typedef void(__cdecl * G_PrintEntities_t)();
|
||||
extern G_PrintEntities_t G_PrintEntities;
|
||||
|
||||
typedef const char*(__cdecl * G_GetEntityTypeName_t)(const gentity_s* ent);
|
||||
extern G_GetEntityTypeName_t G_GetEntityTypeName;
|
||||
|
||||
typedef void(__cdecl * Svcmd_EntityList_f_t)();
|
||||
extern Svcmd_EntityList_f_t Svcmd_EntityList_f;
|
||||
|
||||
typedef void(__cdecl * GScr_LoadGameTypeScript_t)();
|
||||
extern GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript;
|
||||
|
||||
@ -399,7 +423,7 @@ namespace Game
|
||||
typedef void(__cdecl * LargeLocalInit_t)();
|
||||
extern LargeLocalInit_t LargeLocalInit;
|
||||
|
||||
typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, unsigned int size);
|
||||
typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void* ptr, unsigned int size);
|
||||
extern Load_Stream_t Load_Stream;
|
||||
|
||||
typedef void(__cdecl * Load_XString_t)(bool atStreamStart);
|
||||
@ -408,64 +432,64 @@ namespace Game
|
||||
typedef void(__cdecl * Load_XModelPtr_t)(bool atStreamStart);
|
||||
extern Load_XModelPtr_t Load_XModelPtr;
|
||||
|
||||
typedef void(__cdecl * Load_XModelSurfsFixup_t)(XModelSurfs **, XModelLodInfo *);
|
||||
typedef void(__cdecl * Load_XModelSurfsFixup_t)(XModelSurfs**, XModelLodInfo*);
|
||||
extern Load_XModelSurfsFixup_t Load_XModelSurfsFixup;
|
||||
|
||||
typedef void(__cdecl * Load_XStringArray_t)(bool atStreamStart, int count);
|
||||
extern Load_XStringArray_t Load_XStringArray;
|
||||
|
||||
typedef void(__cdecl * Load_XStringCustom_t)(const char **str);
|
||||
typedef void(__cdecl * Load_XStringCustom_t)(const char** str);
|
||||
extern Load_XStringCustom_t Load_XStringCustom;
|
||||
|
||||
typedef void(__cdecl *Load_FxEffectDefHandle_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_FxEffectDefHandle_t)(bool atStreamStart);
|
||||
extern Load_FxEffectDefHandle_t Load_FxEffectDefHandle;
|
||||
|
||||
typedef void(__cdecl *Load_FxElemDef_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_FxElemDef_t)(bool atStreamStart);
|
||||
extern Load_FxElemDef_t Load_FxElemDef;
|
||||
|
||||
typedef void(__cdecl *Load_GfxImagePtr_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_GfxImagePtr_t)(bool atStreamStart);
|
||||
extern Load_GfxImagePtr_t Load_GfxImagePtr;
|
||||
|
||||
typedef void(__cdecl *Load_GfxTextureLoad_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_GfxTextureLoad_t)(bool atStreamStart);
|
||||
extern Load_GfxTextureLoad_t Load_GfxTextureLoad;
|
||||
|
||||
typedef int(__cdecl *Load_Texture_t)(GfxImageLoadDef **loadDef, GfxImage *image);
|
||||
typedef int(__cdecl * Load_Texture_t)(GfxImageLoadDef** loadDef, GfxImage* image);
|
||||
extern Load_Texture_t Load_Texture;
|
||||
|
||||
typedef void(__cdecl * Load_SndAliasCustom_t)(snd_alias_list_t** var);
|
||||
extern Load_SndAliasCustom_t Load_SndAliasCustom;
|
||||
|
||||
typedef void(__cdecl *Load_MaterialHandle_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_MaterialHandle_t)(bool atStreamStart);
|
||||
extern Load_MaterialHandle_t Load_MaterialHandle;
|
||||
|
||||
typedef void(__cdecl *Load_PhysCollmapPtr_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_PhysCollmapPtr_t)(bool atStreamStart);
|
||||
extern Load_PhysCollmapPtr_t Load_PhysCollmapPtr;
|
||||
|
||||
typedef void(__cdecl *Load_PhysPresetPtr_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_PhysPresetPtr_t)(bool atStreamStart);
|
||||
extern Load_PhysPresetPtr_t Load_PhysPresetPtr;
|
||||
|
||||
typedef void(__cdecl *Load_TracerDefPtr_t)(bool atStreamStart);
|
||||
typedef void(__cdecl * Load_TracerDefPtr_t)(bool atStreamStart);
|
||||
extern Load_TracerDefPtr_t Load_TracerDefPtr;
|
||||
|
||||
typedef void(__cdecl *Load_snd_alias_list_nameArray_t)(bool atStreamStart, int count);
|
||||
typedef void(__cdecl * Load_snd_alias_list_nameArray_t)(bool atStreamStart, int count);
|
||||
extern Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray;
|
||||
|
||||
typedef void(__cdecl * Menus_CloseAll_t)(UiContext *dc);
|
||||
typedef void(__cdecl * Menus_CloseAll_t)(UiContext* dc);
|
||||
extern Menus_CloseAll_t Menus_CloseAll;
|
||||
|
||||
typedef void(__cdecl * Menus_CloseRequest_t)(UiContext *dc, menuDef_t* menu);
|
||||
extern Menus_CloseRequest_t Menus_CloseRequest;
|
||||
typedef void(__cdecl * Menus_CloseRequest_t)(UiContext* dc, menuDef_t* menu);
|
||||
extern Menus_CloseRequest_t Menus_CloseRequest;
|
||||
|
||||
typedef int(__cdecl * Menus_OpenByName_t)(UiContext *dc, const char *p);
|
||||
typedef int(__cdecl * Menus_OpenByName_t)(UiContext* dc, const char* p);
|
||||
extern Menus_OpenByName_t Menus_OpenByName;
|
||||
|
||||
typedef menuDef_t *(__cdecl * Menus_FindByName_t)(UiContext *dc, const char *name);
|
||||
typedef menuDef_t *(__cdecl * Menus_FindByName_t)(UiContext* dc, const char* name);
|
||||
extern Menus_FindByName_t Menus_FindByName;
|
||||
|
||||
typedef bool(__cdecl * Menu_IsVisible_t)(UiContext *dc, menuDef_t *menu);
|
||||
typedef bool(__cdecl * Menu_IsVisible_t)(UiContext* dc, menuDef_t* menu);
|
||||
extern Menu_IsVisible_t Menu_IsVisible;
|
||||
|
||||
typedef bool(__cdecl * Menus_MenuIsInStack_t)(UiContext *dc, menuDef_t *menu);
|
||||
typedef bool(__cdecl * Menus_MenuIsInStack_t)(UiContext* dc, menuDef_t* menu);
|
||||
extern Menus_MenuIsInStack_t Menus_MenuIsInStack;
|
||||
|
||||
typedef menuDef_t*(__cdecl * Menu_GetFocused_t)(UiContext* ctx);
|
||||
@ -477,16 +501,16 @@ namespace Game
|
||||
typedef bool(__cdecl * UI_KeyEvent_t)(int clientNum, int key, int down);
|
||||
extern UI_KeyEvent_t UI_KeyEvent;
|
||||
|
||||
typedef const char* (__cdecl * UI_SafeTranslateString_t)(const char* reference);
|
||||
typedef const char*(__cdecl * UI_SafeTranslateString_t)(const char* reference);
|
||||
extern UI_SafeTranslateString_t UI_SafeTranslateString;
|
||||
|
||||
typedef void(__cdecl * UI_ReplaceConversions_t)(const char* sourceString, ConversionArguments* arguments, char* outputString, size_t outputStringSize);
|
||||
extern UI_ReplaceConversions_t UI_ReplaceConversions;
|
||||
|
||||
typedef void(__cdecl * MSG_Init_t)(msg_t *buf, char *data, int length);
|
||||
typedef void(__cdecl * MSG_Init_t)(msg_t* buf, char* data, int length);
|
||||
extern MSG_Init_t MSG_Init;
|
||||
|
||||
typedef void(__cdecl * MSG_ReadData_t)(msg_t *msg, void *data, int len);
|
||||
typedef void(__cdecl * MSG_ReadData_t)(msg_t* msg, void* data, int len);
|
||||
extern MSG_ReadData_t MSG_ReadData;
|
||||
|
||||
typedef int(__cdecl * MSG_ReadLong_t)(msg_t* msg);
|
||||
@ -555,7 +579,7 @@ namespace Game
|
||||
typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr);
|
||||
extern NET_IsLocalAddress_t NET_IsLocalAddress;
|
||||
|
||||
typedef bool(__cdecl * NET_StringToAdr_t)(const char *s, netadr_t *a);
|
||||
typedef int(__cdecl * NET_StringToAdr_t)(const char *s, netadr_t *a);
|
||||
extern NET_StringToAdr_t NET_StringToAdr;
|
||||
|
||||
typedef void(__cdecl * NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char *data);
|
||||
@ -603,7 +627,7 @@ namespace Game
|
||||
typedef void(__cdecl * Playlist_ParsePlaylists_t)(const char* data);
|
||||
extern Playlist_ParsePlaylists_t Playlist_ParsePlaylists;
|
||||
|
||||
typedef Font_s* (__cdecl * R_RegisterFont_t)(const char* asset, int safe);
|
||||
typedef Font_s*(__cdecl * R_RegisterFont_t)(const char* asset, int safe);
|
||||
extern R_RegisterFont_t R_RegisterFont;
|
||||
|
||||
typedef void(__cdecl * R_AddCmdDrawText_t)(const char *text, int maxChars, Font_s *font, float x, float y, float xScale, float yScale, float rotation, const float *color, int style);
|
||||
@ -636,16 +660,22 @@ namespace Game
|
||||
typedef void(__cdecl * RemoveRefToObject_t)(unsigned int id);
|
||||
extern RemoveRefToObject_t RemoveRefToObject;
|
||||
|
||||
typedef void(__cdecl * Scr_AddEntity_t)(gentity_s const*);
|
||||
typedef void(__cdecl * Scr_AddEntity_t)(const gentity_s* ent);
|
||||
extern Scr_AddEntity_t Scr_AddEntity;
|
||||
|
||||
typedef void(__cdecl * Scr_AddString_t)(const char* str);
|
||||
typedef void(__cdecl * Scr_AddString_t)(const char* value);
|
||||
extern Scr_AddString_t Scr_AddString;
|
||||
|
||||
typedef void(__cdecl * Scr_AddInt_t)(int num);
|
||||
typedef void(__cdecl * Scr_AddConstString_t)(unsigned int value);
|
||||
extern Scr_AddConstString_t Scr_AddConstString;
|
||||
|
||||
typedef void(__cdecl * Scr_AddIString_t)(const char* value);
|
||||
extern Scr_AddIString_t Scr_AddIString;
|
||||
|
||||
typedef void(__cdecl * Scr_AddInt_t)(int value);
|
||||
extern Scr_AddInt_t Scr_AddInt;
|
||||
|
||||
typedef void(__cdecl * Scr_AddFloat_t)(float);
|
||||
typedef void(__cdecl * Scr_AddFloat_t)(float value);
|
||||
extern Scr_AddFloat_t Scr_AddFloat;
|
||||
|
||||
typedef void(__cdecl * Scr_AddObject_t)(unsigned int id);
|
||||
@ -660,22 +690,28 @@ namespace Game
|
||||
typedef int(__cdecl * Scr_LoadScript_t)(const char*);
|
||||
extern Scr_LoadScript_t Scr_LoadScript;
|
||||
|
||||
typedef char* (__cdecl * Scr_GetString_t)(int);
|
||||
typedef const char*(__cdecl * Scr_GetString_t)(unsigned int index);
|
||||
extern Scr_GetString_t Scr_GetString;
|
||||
|
||||
typedef float(__cdecl * Scr_GetFloat_t)(int);
|
||||
typedef scr_string_t(__cdecl * Scr_GetConstString_t)(unsigned int index);
|
||||
extern Scr_GetConstString_t Scr_GetConstString;
|
||||
|
||||
typedef const char*(__cdecl * Scr_GetDebugString_t)(unsigned int index);
|
||||
extern Scr_GetDebugString_t Scr_GetDebugString;
|
||||
|
||||
typedef float(__cdecl * Scr_GetFloat_t)(unsigned int index);
|
||||
extern Scr_GetFloat_t Scr_GetFloat;
|
||||
|
||||
typedef int(__cdecl * Scr_GetInt_t)(int);
|
||||
typedef int(__cdecl * Scr_GetInt_t)(unsigned int index);
|
||||
extern Scr_GetInt_t Scr_GetInt;
|
||||
|
||||
typedef unsigned int(__cdecl * Scr_GetObject_t)(int);
|
||||
typedef unsigned int(__cdecl * Scr_GetObject_t)(unsigned int index);
|
||||
extern Scr_GetObject_t Scr_GetObject;
|
||||
|
||||
typedef unsigned int(__cdecl * Scr_GetNumParam_t)();
|
||||
extern Scr_GetNumParam_t Scr_GetNumParam;
|
||||
|
||||
typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char*, const char*);
|
||||
typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char* filename, const char* name);
|
||||
extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle;
|
||||
|
||||
typedef int(__cdecl * Scr_ExecThread_t)(int, int);
|
||||
@ -693,18 +729,45 @@ namespace Game
|
||||
typedef void(__cdecl * Scr_ClearOutParams_t)();
|
||||
extern Scr_ClearOutParams_t Scr_ClearOutParams;
|
||||
|
||||
typedef void(__cdecl * Scr_RegisterFunction_t)(scr_function_t function);
|
||||
typedef void(__cdecl * Scr_RegisterFunction_t)(int func, const char* name);
|
||||
extern Scr_RegisterFunction_t Scr_RegisterFunction;
|
||||
|
||||
typedef bool(__cdecl * Scr_IsSystemActive_t)();
|
||||
extern Scr_IsSystemActive_t Scr_IsSystemActive;
|
||||
|
||||
typedef int(__cdecl* Scr_GetType_t)(unsigned int);
|
||||
typedef int(__cdecl * Scr_GetType_t)(unsigned int);
|
||||
extern Scr_GetType_t Scr_GetType;
|
||||
|
||||
typedef void(__cdecl* Scr_Error_t)(const char*);
|
||||
typedef void(__cdecl * Scr_Error_t)(const char*);
|
||||
extern Scr_Error_t Scr_Error;
|
||||
|
||||
typedef void(__cdecl * Scr_ObjectError_t)(const char*);
|
||||
extern Scr_ObjectError_t Scr_ObjectError;
|
||||
|
||||
typedef void(__cdecl * Scr_ParamError_t)(unsigned int paramIndex, const char*);
|
||||
extern Scr_ParamError_t Scr_ParamError;
|
||||
|
||||
typedef void(__cdecl * Scr_GetObjectField_t)(unsigned int classnum, int entnum, int offset);
|
||||
extern Scr_GetObjectField_t Scr_GetObjectField;
|
||||
|
||||
typedef int(__cdecl * Scr_SetObjectField_t)(unsigned int classnum, int entnum, int offset);
|
||||
extern Scr_SetObjectField_t Scr_SetObjectField;
|
||||
|
||||
typedef void(__cdecl * Scr_SetClientField_t)(gclient_s* client, int offset);
|
||||
extern Scr_SetClientField_t Scr_SetClientField;
|
||||
|
||||
typedef void(__cdecl * Scr_GetEntityField_t)(int entnum, int offset);
|
||||
extern Scr_GetEntityField_t Scr_GetEntityField;
|
||||
|
||||
typedef void(__cdecl * Scr_AddClassField_t)(unsigned int classnum, const char* name, unsigned int offset);
|
||||
extern Scr_AddClassField_t Scr_AddClassField;
|
||||
|
||||
typedef gentity_s*(__cdecl * GetPlayerEntity_t)(scr_entref_t entref);
|
||||
extern GetPlayerEntity_t GetPlayerEntity;
|
||||
|
||||
typedef gentity_s*(__cdecl * GetEntity_t)(scr_entref_t entref);
|
||||
extern GetEntity_t GetEntity;
|
||||
|
||||
typedef script_t* (__cdecl * Script_Alloc_t)(int length);
|
||||
extern Script_Alloc_t Script_Alloc;
|
||||
|
||||
@ -714,21 +777,27 @@ namespace Game
|
||||
typedef int(__cdecl * Script_CleanString_t)(char* buffer);
|
||||
extern Script_CleanString_t Script_CleanString;
|
||||
|
||||
typedef char* (__cdecl * SE_Load_t)(const char* file, int Unk);
|
||||
typedef char*(__cdecl * SE_Load_t)(const char* file, int Unk);
|
||||
extern SE_Load_t SE_Load;
|
||||
|
||||
typedef char* (__cdecl * SEH_StringEd_GetString_t)(const char* string);
|
||||
typedef char*(__cdecl * SEH_StringEd_GetString_t)(const char* string);
|
||||
extern SEH_StringEd_GetString_t SEH_StringEd_GetString;
|
||||
|
||||
typedef unsigned int(__cdecl* SEH_ReadCharFromString_t)(const char** text, int* isTrailingPunctuation);
|
||||
extern SEH_ReadCharFromString_t SEH_ReadCharFromString;
|
||||
|
||||
typedef char* (__cdecl * SL_ConvertToString_t)(unsigned short stringValue);
|
||||
typedef const char*(__cdecl * SL_ConvertToString_t)(scr_string_t stringValue);
|
||||
extern SL_ConvertToString_t SL_ConvertToString;
|
||||
|
||||
typedef short(__cdecl * SL_GetString_t)(const char *str, unsigned int user);
|
||||
extern SL_GetString_t SL_GetString;
|
||||
|
||||
typedef void(__cdecl * SL_AddRefToString_t)(unsigned int stringValue);
|
||||
extern SL_AddRefToString_t SL_AddRefToString;
|
||||
|
||||
typedef void(__cdecl * SL_RemoveRefToString_t)(unsigned int stringValue);
|
||||
extern SL_RemoveRefToString_t SL_RemoveRefToString;
|
||||
|
||||
typedef void(__cdecl * SND_Init_t)(int a1, int a2, int a3);
|
||||
extern SND_Init_t SND_Init;
|
||||
|
||||
@ -741,15 +810,21 @@ namespace Game
|
||||
typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char);
|
||||
extern Steam_JoinLobby_t Steam_JoinLobby;
|
||||
|
||||
typedef const char*(__cdecl * StringTable_Lookup_t)(StringTable *table, const int comparisonColumn, const char *value, const int valueColumn);
|
||||
typedef const char*(__cdecl * StringTable_Lookup_t)(const StringTable *table, const int comparisonColumn, const char *value, const int valueColumn);
|
||||
extern StringTable_Lookup_t StringTable_Lookup;
|
||||
|
||||
typedef const char* (__cdecl * StringTable_GetColumnValueForRow_t)(const StringTable* table, int, int column);
|
||||
extern StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow;
|
||||
|
||||
typedef int(__cdecl * StringTable_HashString_t)(const char* string);
|
||||
extern StringTable_HashString_t StringTable_HashString;
|
||||
|
||||
typedef gentity_t*(__cdecl* SV_AddTestClient_t)();
|
||||
extern SV_AddTestClient_t SV_AddTestClient;
|
||||
|
||||
typedef int(__cdecl * SV_IsTestClient_t)(int clientNum);
|
||||
extern SV_IsTestClient_t SV_IsTestClient;
|
||||
|
||||
typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID);
|
||||
extern SV_GameClientNum_Score_t SV_GameClientNum_Score;
|
||||
|
||||
@ -777,13 +852,16 @@ namespace Game
|
||||
typedef void(__cdecl * SV_ClientThink_t)(client_s*, usercmd_s*);
|
||||
extern SV_ClientThink_t SV_ClientThink;
|
||||
|
||||
typedef void(__cdecl * SV_DropClient_t)(client_t* drop, const char* reason, bool tellThem);
|
||||
extern SV_DropClient_t SV_DropClient;
|
||||
|
||||
typedef client_t*(__cdecl * SV_GetPlayerByName_t)();
|
||||
extern SV_GetPlayerByName_t SV_GetPlayerByName;
|
||||
|
||||
typedef client_t*(__cdecl * SV_GetPlayerByNum_t)();
|
||||
extern SV_GetPlayerByNum_t SV_GetPlayerByNum;
|
||||
|
||||
typedef int(__cdecl * Sys_Error_t)(int, char *, ...);
|
||||
typedef void(__cdecl * Sys_Error_t)(const char* error, ...);
|
||||
extern Sys_Error_t Sys_Error;
|
||||
|
||||
typedef void(__cdecl * Sys_FreeFileList_t)(char** list);
|
||||
@ -834,16 +912,16 @@ namespace Game
|
||||
typedef void(__cdecl * Sys_SuspendOtherThreads_t)();
|
||||
extern Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads;
|
||||
|
||||
typedef void(__cdecl * UI_AddMenuList_t)(UiContext *dc, MenuList *menuList, int close);
|
||||
typedef void(__cdecl * UI_AddMenuList_t)(UiContext* dc, MenuList* menuList, int close);
|
||||
extern UI_AddMenuList_t UI_AddMenuList;
|
||||
|
||||
typedef uiMenuCommand_t(__cdecl * UI_GetActiveMenu_t)(int localClientNum);
|
||||
extern UI_GetActiveMenu_t UI_GetActiveMenu;
|
||||
|
||||
typedef char* (__cdecl * UI_CheckStringTranslation_t)(char*, char*);
|
||||
typedef char*(__cdecl * UI_CheckStringTranslation_t)(char*, char*);
|
||||
extern UI_CheckStringTranslation_t UI_CheckStringTranslation;
|
||||
|
||||
typedef MenuList *(__cdecl * UI_LoadMenus_t)(const char *menuFile, int imageTrack);
|
||||
typedef MenuList*(__cdecl * UI_LoadMenus_t)(const char* menuFile, int imageTrack);
|
||||
extern UI_LoadMenus_t UI_LoadMenus;
|
||||
|
||||
typedef void(__cdecl * UI_UpdateArenas_t)();
|
||||
@ -855,13 +933,13 @@ namespace Game
|
||||
typedef void(__cdecl * UI_DrawHandlePic_t)(/*ScreenPlacement*/void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Material *material);
|
||||
extern UI_DrawHandlePic_t UI_DrawHandlePic;
|
||||
|
||||
typedef ScreenPlacement* (__cdecl * ScrPlace_GetActivePlacement_t)(int localClientNum);
|
||||
typedef ScreenPlacement*(__cdecl * ScrPlace_GetActivePlacement_t)(int localClientNum);
|
||||
extern ScrPlace_GetActivePlacement_t ScrPlace_GetActivePlacement;
|
||||
|
||||
typedef int(__cdecl * UI_TextWidth_t)(const char *text, int maxChars, Font_s *font, float scale);
|
||||
extern UI_TextWidth_t UI_TextWidth;
|
||||
|
||||
typedef void(__cdecl * UI_DrawText_t)(void* scrPlace, const char *text, int maxChars, Font_s *font, float x, float y, int horzAlign, int vertAlign, float scale, const float *color, int style);
|
||||
typedef void(__cdecl * UI_DrawText_t)(const ScreenPlacement* scrPlace, const char* text, int maxChars, Font_s* font, float x, float y, int horzAlign, int vertAlign, float scale, const float* color, int style);
|
||||
extern UI_DrawText_t UI_DrawText;
|
||||
|
||||
typedef Font_s* (__cdecl* UI_GetFontHandle_t)(ScreenPlacement* scrPlace, int fontEnum, float scale);
|
||||
@ -927,6 +1005,21 @@ namespace Game
|
||||
typedef EffectiveStance(__cdecl * PM_GetEffectiveStance_t)(const playerState_s* ps);
|
||||
extern PM_GetEffectiveStance_t PM_GetEffectiveStance;
|
||||
|
||||
typedef int(__cdecl * CL_MouseEvent_t)(int x, int y, int dx, int dy);
|
||||
extern CL_MouseEvent_t CL_MouseEvent;
|
||||
|
||||
typedef void(__cdecl * IN_RecenterMouse_t)();
|
||||
extern IN_RecenterMouse_t IN_RecenterMouse;
|
||||
|
||||
typedef void(__cdecl * IN_MouseMove_t)();
|
||||
extern IN_MouseMove_t IN_MouseMove;
|
||||
|
||||
typedef void(__cdecl * IN_Init_t)();
|
||||
extern IN_Init_t IN_Init;
|
||||
|
||||
typedef void(__cdecl * IN_Shutdown_t)();
|
||||
extern IN_Shutdown_t IN_Shutdown;
|
||||
|
||||
extern XAssetHeader* DB_XAssetPool;
|
||||
extern unsigned int* g_poolSize;
|
||||
|
||||
@ -940,13 +1033,13 @@ namespace Game
|
||||
extern float* cgameFOVSensitivityScale;
|
||||
|
||||
extern int* svs_time;
|
||||
extern int* svs_numclients;
|
||||
extern int* svs_clientCount;
|
||||
extern client_t* svs_clients;
|
||||
|
||||
extern source_t **sourceFiles;
|
||||
extern keywordHash_t **menuParseKeywordHash;
|
||||
|
||||
extern UiContext *uiContext;
|
||||
extern UiContext* uiContext;
|
||||
|
||||
extern int* arenaCount;
|
||||
extern mapArena_t* arenas;
|
||||
@ -1012,6 +1105,7 @@ namespace Game
|
||||
extern unsigned short* db_hashTable;
|
||||
|
||||
extern scrVmPub_t* scrVmPub;
|
||||
extern scrVarPub_t* scrVarPub;
|
||||
|
||||
extern clientstate_t* clcState;
|
||||
|
||||
@ -1043,13 +1137,29 @@ namespace Game
|
||||
constexpr auto AIM_ASSIST_GRAPH_COUNT = 4u;
|
||||
extern GraphFloat* aaInputGraph;
|
||||
|
||||
extern const char* MY_CMDS;
|
||||
|
||||
constexpr auto MAX_MODELS = 512;
|
||||
extern XModel** cached_models;
|
||||
|
||||
extern vec3_t* CorrectSolidDeltas;
|
||||
|
||||
extern FastCriticalSection* db_hashCritSect;
|
||||
|
||||
extern level_locals_t* level;
|
||||
|
||||
extern float(*penetrationDepthTable)[PENETRATE_TYPE_COUNT][SURF_TYPE_COUNT];
|
||||
|
||||
extern WinMouseVars_t* s_wmv;
|
||||
|
||||
extern int* window_center_x;
|
||||
extern int* window_center_y;
|
||||
|
||||
void Sys_LockRead(FastCriticalSection* critSect);
|
||||
void Sys_UnlockRead(FastCriticalSection* critSect);
|
||||
|
||||
XModel* G_GetModel(int index);
|
||||
|
||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
|
||||
void Menu_FreeItemMemory(Game::itemDef_s* item);
|
||||
void Menu_SetNextCursorItem(Game::UiContext* ctx, Game::menuDef_t* currentMenu, int unk = 1);
|
||||
@ -1069,7 +1179,7 @@ namespace Game
|
||||
|
||||
void FS_AddLocalizedGameDirectory(const char *path, const char *dir);
|
||||
|
||||
bool PM_IsAdsAllowed(Game::playerState_s* playerState);
|
||||
bool PM_IsAdsAllowed(playerState_s* ps);
|
||||
|
||||
void ShowMessageBox(const std::string& message, const std::string& title);
|
||||
|
||||
@ -1078,12 +1188,15 @@ namespace Game
|
||||
void R_LoadSunThroughDvars(const char* mapname, sunflare_t* sun);
|
||||
void R_SetSunFromDvars(sunflare_t* sun);
|
||||
|
||||
void SV_KickClient(client_t* client, const char* reason);
|
||||
void SV_KickClientError(client_t* client, const std::string& reason);
|
||||
void SV_GameDropClient(int clientNum, const char* reason);
|
||||
void SV_DropAllBots();
|
||||
void SV_BotUserMove(client_t* client);
|
||||
|
||||
void RuntimeErrorInternal(int channel, const char* codePos, unsigned int index, const char* msg);
|
||||
void IncInParam();
|
||||
|
||||
void Scr_iPrintLn(int clientNum, const std::string& message);
|
||||
void Scr_iPrintLnBold(int clientNum, const std::string& message);
|
||||
void Scr_NotifyId(unsigned int id, unsigned __int16 stringValue, unsigned int paramcount);
|
||||
void Scr_AddBool(int value);
|
||||
|
||||
void IN_KeyUp(kbutton_t* button);
|
||||
void IN_KeyDown(kbutton_t* button);
|
||||
|
@ -20,8 +20,30 @@ namespace Game
|
||||
typedef vec_t vec3_t[3];
|
||||
typedef vec_t vec4_t[4];
|
||||
|
||||
typedef unsigned int scr_entref_t;
|
||||
typedef void(__cdecl * scr_function_t)(scr_entref_t);
|
||||
typedef unsigned __int16 scr_string_t;
|
||||
|
||||
struct scr_entref_t
|
||||
{
|
||||
unsigned __int16 entnum;
|
||||
unsigned __int16 classnum;
|
||||
};
|
||||
|
||||
typedef void(__cdecl * BuiltinFunction)();
|
||||
typedef void(__cdecl * BuiltinMethod)(scr_entref_t);
|
||||
|
||||
struct BuiltinFunctionDef
|
||||
{
|
||||
const char* actionString;
|
||||
BuiltinFunction actionFunc;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct BuiltinMethodDef
|
||||
{
|
||||
const char* actionString;
|
||||
BuiltinMethod actionFunc;
|
||||
int type;
|
||||
};
|
||||
|
||||
enum XAssetType
|
||||
{
|
||||
@ -74,6 +96,43 @@ namespace Game
|
||||
ASSET_TYPE_INVALID = -1,
|
||||
};
|
||||
|
||||
enum materialSurfType_t
|
||||
{
|
||||
SURF_TYPE_DEFAULT,
|
||||
SURF_TYPE_BARK,
|
||||
SURF_TYPE_BRICK,
|
||||
SURF_TYPE_CARPET,
|
||||
SURF_TYPE_CLOTH,
|
||||
SURF_TYPE_CONCRETE,
|
||||
SURF_TYPE_DIRT,
|
||||
SURF_TYPE_FLESH,
|
||||
SURF_TYPE_FOLIAGE,
|
||||
SURF_TYPE_GLASS,
|
||||
SURF_TYPE_GRASS,
|
||||
SURF_TYPE_GRAVEL,
|
||||
SURF_TYPE_ICE,
|
||||
SURF_TYPE_METAL,
|
||||
SURF_TYPE_MUD,
|
||||
SURF_TYPE_PAPER,
|
||||
SURF_TYPE_PLASTER,
|
||||
SURF_TYPE_ROCK,
|
||||
SURF_TYPE_SAND,
|
||||
SURF_TYPE_SNOW,
|
||||
SURF_TYPE_WATER,
|
||||
SURF_TYPE_WOOD,
|
||||
SURF_TYPE_ASPHALT,
|
||||
SURF_TYPE_CERAMIC,
|
||||
SURF_TYPE_PLASTIC,
|
||||
SURF_TYPE_RUBBER,
|
||||
SURF_TYPE_CUSHION,
|
||||
SURF_TYPE_FRUIT,
|
||||
SURF_TYPE_PAINTED_METAL,
|
||||
SURF_TYPE_RIOT_SHIELD,
|
||||
SURF_TYPE_SLUSH,
|
||||
|
||||
SURF_TYPE_COUNT
|
||||
};
|
||||
|
||||
enum dvar_flag : unsigned __int16
|
||||
{
|
||||
DVAR_NONE = 0x0, // No flags
|
||||
@ -209,6 +268,17 @@ namespace Game
|
||||
FL_MOVER_SLIDE = 0x8000000
|
||||
};
|
||||
|
||||
enum ClassNum : unsigned int
|
||||
{
|
||||
CLASS_NUM_ENTITY = 0x0,
|
||||
CLASS_NUM_HUDELEM = 0x1,
|
||||
CLASS_NUM_PATHNODE = 0x2,
|
||||
CLASS_NUM_VEHICLENODE = 0x3,
|
||||
CLASS_NUM_VEHTRACK_SEGMENT = 0x4,
|
||||
CLASS_NUM_FXENTITY = 0x5,
|
||||
CLASS_NUM_COUNT = 0x6,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HITLOC_NONE,
|
||||
@ -1451,21 +1521,23 @@ namespace Game
|
||||
|
||||
enum usercmdButtonBits
|
||||
{
|
||||
CMD_BUTTON_ATTACK = 0x1,
|
||||
CMD_BUTTON_SPRINT = 0x2,
|
||||
CMD_BUTTON_MELEE = 0x4,
|
||||
CMD_BUTTON_ACTIVATE = 0x8,
|
||||
CMD_BUTTON_RELOAD = 0x10,
|
||||
CMD_BUTTON_USE_RELOAD = 0x20,
|
||||
CMD_BUTTON_PRONE = 0x100,
|
||||
CMD_BUTTON_CROUCH = 0x200,
|
||||
CMD_BUTTON_UP = 0x400,
|
||||
CMD_BUTTON_ADS = 0x800,
|
||||
CMD_BUTTON_DOWN = 0x1000,
|
||||
CMD_BUTTON_BREATH = 0x2000,
|
||||
CMD_BUTTON_FRAG = 0x4000,
|
||||
CMD_BUTTON_OFFHAND_SECONDARY = 0x8000,
|
||||
CMD_BUTTON_THROW = 0x80000,
|
||||
CMD_BUTTON_ATTACK = 0x1,
|
||||
CMD_BUTTON_SPRINT = 0x2,
|
||||
CMD_BUTTON_MELEE = 0x4,
|
||||
CMD_BUTTON_ACTIVATE = 0x8,
|
||||
CMD_BUTTON_RELOAD = 0x10,
|
||||
CMD_BUTTON_USE_RELOAD = 0x20,
|
||||
CMD_BUTTON_LEAN_LEFT = 0x40,
|
||||
CMD_BUTTON_LEAN_RIGHT = 0x80,
|
||||
CMD_BUTTON_PRONE = 0x100,
|
||||
CMD_BUTTON_CROUCH = 0x200,
|
||||
CMD_BUTTON_UP = 0x400,
|
||||
CMD_BUTTON_ADS = 0x800,
|
||||
CMD_BUTTON_DOWN = 0x1000,
|
||||
CMD_BUTTON_BREATH = 0x2000,
|
||||
CMD_BUTTON_FRAG = 0x4000,
|
||||
CMD_BUTTON_OFFHAND_SECONDARY = 0x8000,
|
||||
CMD_BUTTON_THROW = 0x80000
|
||||
};
|
||||
|
||||
#pragma pack(push, 4)
|
||||
@ -4999,6 +5071,21 @@ namespace Game
|
||||
int dataCount;
|
||||
} gameState;
|
||||
|
||||
struct HunkUser
|
||||
{
|
||||
HunkUser* current;
|
||||
HunkUser* next;
|
||||
int maxSize;
|
||||
int end;
|
||||
int pos;
|
||||
const char* name;
|
||||
bool fixed;
|
||||
int type;
|
||||
char buf[1];
|
||||
};
|
||||
|
||||
static_assert(sizeof(HunkUser) == 36);
|
||||
|
||||
struct VariableStackBuffer
|
||||
{
|
||||
const char *pos;
|
||||
@ -5094,6 +5181,40 @@ namespace Game
|
||||
VariableValue stack[2048];
|
||||
};
|
||||
|
||||
struct scrVarPub_t
|
||||
{
|
||||
const char* fieldBuffer;
|
||||
unsigned __int16 canonicalStrCount;
|
||||
bool developer_script;
|
||||
bool evaluate;
|
||||
const char* error_message;
|
||||
int error_index;
|
||||
int time;
|
||||
int timeArrayId;
|
||||
int pauseArrayId;
|
||||
int notifyArrayId;
|
||||
int objectStackId;
|
||||
int levelId;
|
||||
int gameId;
|
||||
int animId;
|
||||
int freeEntList;
|
||||
int tempVariable;
|
||||
int numScriptValues[2];
|
||||
bool bInited;
|
||||
unsigned __int16 savecount;
|
||||
unsigned __int16 savecountMark;
|
||||
int checksum;
|
||||
int entId;
|
||||
int entFieldName;
|
||||
HunkUser* programHunkUser;
|
||||
const char* programBuffer;
|
||||
const char* endScriptBuffer;
|
||||
unsigned __int16 saveIdMap[36864];
|
||||
unsigned __int16 saveIdMapRev[36864];
|
||||
};
|
||||
|
||||
static_assert(sizeof(scrVarPub_t) == 0x24060);
|
||||
|
||||
enum UILocalVarType
|
||||
{
|
||||
UILOCALVAR_INT = 0x0,
|
||||
@ -5503,15 +5624,25 @@ namespace Game
|
||||
CON_CONNECTED = 0x2
|
||||
} clientConnected_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VISIONSET_NORMAL,
|
||||
VISIONSET_NIGHT,
|
||||
VISIONSET_MISSILECAM,
|
||||
VISIONSET_THERMAL,
|
||||
VISIONSET_PAIN,
|
||||
VISIONSETCOUNT
|
||||
} visionSetMode_t;
|
||||
|
||||
typedef struct gclient_s
|
||||
{
|
||||
playerState_s ps;
|
||||
sessionState_t sessionState; // 12572
|
||||
char pad0[40];
|
||||
unsigned char __pad0[40];
|
||||
clientConnected_t connected; // 12616
|
||||
char pad1[144];
|
||||
unsigned int team; // 12764
|
||||
char pad2[436];
|
||||
unsigned char __pad1[144];
|
||||
team_t team; // 12764
|
||||
unsigned char __pad2[436];
|
||||
int flags; // 13204
|
||||
int spectatorClient;
|
||||
int lastCmdTime;
|
||||
@ -5519,7 +5650,10 @@ namespace Game
|
||||
int oldbuttons; // 13220
|
||||
int latched_buttons; // 13224
|
||||
int buttonsSinceLastFrame; // 13228
|
||||
char pad3[700]; // 13232
|
||||
unsigned char __pad3[324]; // 13232
|
||||
int visionDuration[5];
|
||||
char visionName[5][64];
|
||||
unsigned char __pad4[36];
|
||||
} gclient_t;
|
||||
|
||||
static_assert(sizeof(gclient_t) == 13932);
|
||||
@ -5619,6 +5753,53 @@ namespace Game
|
||||
|
||||
static_assert(sizeof(gentity_s) == 0x274);
|
||||
|
||||
enum $1C4253065710F064DA9E4D59ED6EC544
|
||||
{
|
||||
ENTFIELD_ENTITY = 0x0,
|
||||
ENTFIELD_SENTIENT = 0x2000,
|
||||
ENTFIELD_ACTOR = 0x4000,
|
||||
ENTFIELD_CLIENT = 0x6000,
|
||||
ENTFIELD_VEHICLE = 0x8000,
|
||||
ENTFIELD_MASK = 0xE000,
|
||||
};
|
||||
|
||||
enum fieldtype_t
|
||||
{
|
||||
F_INT = 0x0,
|
||||
F_SHORT = 0x1,
|
||||
F_BYTE = 0x2,
|
||||
F_FLOAT = 0x3,
|
||||
F_CSTRING = 0x4,
|
||||
F_STRING = 0x5,
|
||||
F_VECTOR = 0x6,
|
||||
F_ENTITY = 0x7,
|
||||
F_ENTHANDLE = 0x8,
|
||||
F_ANGLES_YAW = 0x9,
|
||||
F_OBJECT = 0xA,
|
||||
F_MODEL = 0xB,
|
||||
};
|
||||
|
||||
struct ent_field_t
|
||||
{
|
||||
const char* name;
|
||||
int ofs;
|
||||
fieldtype_t type;
|
||||
void(__cdecl * setter)(gentity_s*, int);
|
||||
void(__cdecl * getter)(gentity_s*, int);
|
||||
};
|
||||
|
||||
struct client_fields_s
|
||||
{
|
||||
const char* name;
|
||||
int ofs;
|
||||
fieldtype_t type;
|
||||
void(__cdecl * setter)(gclient_s*, const client_fields_s*);
|
||||
void(__cdecl * getter)(gclient_s*, const client_fields_s*);
|
||||
};
|
||||
|
||||
typedef void(__cdecl * ScriptCallbackEnt)(gentity_s*, int);
|
||||
typedef void(__cdecl * ScriptCallbackClient)(gclient_s*, const client_fields_s*);
|
||||
|
||||
struct lockonFireParms
|
||||
{
|
||||
bool lockon;
|
||||
@ -5661,7 +5842,7 @@ namespace Game
|
||||
int pureAuthentic; // 135896
|
||||
char __pad7[133138]; // 135900
|
||||
short scriptID; // 269038
|
||||
int isBot; // 269040
|
||||
int bIsTestClient; // 269040
|
||||
int serverID; // 269044
|
||||
char __pad8[9224]; // 269048
|
||||
unsigned __int64 steamID; // 278272
|
||||
@ -6880,7 +7061,7 @@ namespace Game
|
||||
SHELLSHOCK_VIEWTYPE_NONE = 0x2,
|
||||
};
|
||||
|
||||
struct shellshock_parms_t
|
||||
struct shellshock_parms_t
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -7196,6 +7377,149 @@ namespace Game
|
||||
TempPriority tempPriority;
|
||||
};
|
||||
|
||||
struct trigger_info_t
|
||||
{
|
||||
unsigned __int16 entnum;
|
||||
unsigned __int16 otherEntnum;
|
||||
int useCount;
|
||||
int otherUseCount;
|
||||
};
|
||||
|
||||
struct com_parse_mark_t
|
||||
{
|
||||
int lines;
|
||||
const char* text;
|
||||
int ungetToken;
|
||||
int backup_lines;
|
||||
const char* backup_text;
|
||||
};
|
||||
|
||||
struct cached_tag_mat_t
|
||||
{
|
||||
int time;
|
||||
int entnum;
|
||||
unsigned __int16 name;
|
||||
float tagMat[4][3];
|
||||
};
|
||||
|
||||
struct Turret
|
||||
{
|
||||
bool inuse;
|
||||
int flags;
|
||||
int fireTime;
|
||||
float arcmin[2];
|
||||
float arcmax[2];
|
||||
float dropPitch;
|
||||
int stance;
|
||||
int prevStance;
|
||||
int fireSndDelay;
|
||||
float userOrigin[3];
|
||||
float playerSpread;
|
||||
int state;
|
||||
EntHandle target;
|
||||
float targetOffset[3];
|
||||
EntHandle manualTarget;
|
||||
float manualTargetOffset[3];
|
||||
int targetTime;
|
||||
int stateChangeTime;
|
||||
int modeChangeTime;
|
||||
float maxRangeSquared;
|
||||
int prevTargetIndex;
|
||||
team_t eTeam;
|
||||
int convergenceTime[2];
|
||||
float targetPos[3];
|
||||
float missOffsetNormalized[3];
|
||||
float scanSpeed;
|
||||
float scanDecelYaw;
|
||||
int scanPauseTime;
|
||||
bool triggerDown;
|
||||
float heatLevel;
|
||||
int heatPenaltyEndTime;
|
||||
float barrelRollRate;
|
||||
int autoRotationStopDelay;
|
||||
int lastAutoRotationRequestTime;
|
||||
unsigned __int8 fireSnd;
|
||||
unsigned __int8 fireSndPlayer;
|
||||
unsigned __int8 stopSnd;
|
||||
unsigned __int8 stopSndPlayer;
|
||||
unsigned __int8 scanSnd;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Turret) == 0xC4);
|
||||
|
||||
struct level_locals_t
|
||||
{
|
||||
gclient_s* clients;
|
||||
gentity_s* gentities;
|
||||
int num_entities;
|
||||
gentity_s* firstFreeEnt;
|
||||
gentity_s* lastFreeEnt;
|
||||
Turret* turrets;
|
||||
void* logFile;
|
||||
int initializing;
|
||||
int clientIsSpawning;
|
||||
objective_t objectives[32];
|
||||
int maxclients;
|
||||
int framenum;
|
||||
int time;
|
||||
int previousTime;
|
||||
int frametime;
|
||||
int startTime;
|
||||
int teamScores[4];
|
||||
int lastTeammateHealthTime;
|
||||
int bUpdateScoresForIntermission;
|
||||
bool teamHasRadar[4];
|
||||
bool teamRadarBlocked[4];
|
||||
int manualNameChange;
|
||||
int numConnectedClients;
|
||||
int sortedClients[18];
|
||||
char voteString[1024];
|
||||
char voteDisplayString[1024];
|
||||
int voteTime;
|
||||
int voteExecuteTime;
|
||||
int voteYes;
|
||||
int voteNo;
|
||||
int numVotingClients;
|
||||
SpawnVar spawnVar;
|
||||
int savepersist;
|
||||
EntHandle droppedWeaponCue[32];
|
||||
float fFogOpaqueDist;
|
||||
float fFogOpaqueDistSqrd;
|
||||
int currentPlayerClone;
|
||||
trigger_info_t pendingTriggerList[256];
|
||||
trigger_info_t currentTriggerList[256];
|
||||
int pendingTriggerListSize;
|
||||
int currentTriggerListSize;
|
||||
int finished;
|
||||
int bPlayerIgnoreRadiusDamage;
|
||||
int bPlayerIgnoreRadiusDamageLatched;
|
||||
int registerWeapons;
|
||||
int bRegisterItems;
|
||||
int currentEntityThink;
|
||||
void* openScriptIOFileHandles[1];
|
||||
char* openScriptIOFileBuffers[1];
|
||||
com_parse_mark_t currentScriptIOLineMark[1];
|
||||
cached_tag_mat_t cachedTagMat;
|
||||
int scriptPrintChannel;
|
||||
float compassMapUpperLeft[2];
|
||||
float compassMapWorldSize[2];
|
||||
float compassNorth[2];
|
||||
void* vehicles;
|
||||
int hudElemLastAssignedSoundID;
|
||||
};
|
||||
|
||||
static_assert(sizeof(level_locals_t) == 0x2F78);
|
||||
|
||||
struct WinMouseVars_t
|
||||
{
|
||||
int oldButtonState;
|
||||
tagPOINT oldPos;
|
||||
bool mouseActive;
|
||||
bool mouseInitialized;
|
||||
};
|
||||
|
||||
static_assert(sizeof(WinMouseVars_t) == 0x10);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#ifndef IDA
|
||||
|
25
src/Main.cpp
25
src/Main.cpp
@ -55,7 +55,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l
|
||||
Steam::Proxy::RunMod();
|
||||
|
||||
// Ensure we're working with our desired binary
|
||||
char* _module = reinterpret_cast<char*>(0x400000);
|
||||
auto* _module = reinterpret_cast<char*>(0x400000);
|
||||
auto hash1 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x1000, 0x2D531F); // .text
|
||||
auto hash2 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x2D75FC, 0xBDA04); // .rdata
|
||||
if ((hash1 != 0x54684DBE
|
||||
@ -67,17 +67,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
[]()
|
||||
{
|
||||
if (!Components::Dedicated::IsEnabled() && !Components::Loader::IsPerformingUnitTests())
|
||||
{
|
||||
Components::AntiCheat::ProtectProcess();
|
||||
Components::AntiCheat::PatchThreadCreation();
|
||||
}
|
||||
}();
|
||||
#endif
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment
|
||||
|
||||
@ -88,18 +77,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l
|
||||
{
|
||||
Main::Uninitialize();
|
||||
}
|
||||
else if (ul_reason_for_call == DLL_THREAD_ATTACH)
|
||||
{
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
[]()
|
||||
{
|
||||
if (!Components::Dedicated::IsEnabled() && !Components::Loader::IsPerformingUnitTests())
|
||||
{
|
||||
Components::AntiCheat::VerifyThreadIntegrity();
|
||||
}
|
||||
}();
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -90,17 +90,6 @@
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
// VMProtect
|
||||
// #define USE_VMP
|
||||
#ifdef USE_VMP
|
||||
#include <VMProtect/VMProtectSDK.h>
|
||||
#define __VMProtectBeginUltra VMProtectBeginUltra
|
||||
#define __VMProtectEnd VMProtectEnd()
|
||||
#else
|
||||
#define __VMProtectBeginUltra
|
||||
#define __VMProtectEnd
|
||||
#endif
|
||||
|
||||
// Protobuf
|
||||
#include "proto/session.pb.h"
|
||||
#include "proto/party.pb.h"
|
||||
|
@ -23,16 +23,24 @@ namespace Utils
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ToLower(std::string input)
|
||||
std::string ToLower(std::string text)
|
||||
{
|
||||
std::transform(input.begin(), input.end(), input.begin(), ::tolower);
|
||||
return input;
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
|
||||
{
|
||||
return static_cast<char>(std::tolower(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string ToUpper(std::string input)
|
||||
std::string ToUpper(std::string text)
|
||||
{
|
||||
std::transform(input.begin(), input.end(), input.begin(), ::toupper);
|
||||
return input;
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
|
||||
{
|
||||
return static_cast<char>(std::toupper(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string DumpHex(const std::string& data, const std::string& separator)
|
||||
@ -89,12 +97,13 @@ namespace Utils
|
||||
|
||||
bool StartsWith(const std::string& haystack, const std::string& needle)
|
||||
{
|
||||
return (haystack.size() >= needle.size() && haystack.substr(0, needle.size()) == needle);
|
||||
return haystack.find(needle) == 0; // If the pos of the first found char is 0, string starts with 'needle'
|
||||
}
|
||||
|
||||
bool EndsWith(const std::string& haystack, const std::string& needle)
|
||||
{
|
||||
return (haystack.size() >= needle.size() && haystack.substr(haystack.size() - needle.size()) == needle);
|
||||
if (needle.size() > haystack.size()) return false;
|
||||
return std::equal(needle.rbegin(), needle.rend(), haystack.rbegin());
|
||||
}
|
||||
|
||||
int IsSpace(int c)
|
||||
|
@ -11,7 +11,7 @@ namespace Utils
|
||||
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
|
||||
|
||||
VAProvider() : currentBuffer(0) {}
|
||||
~VAProvider() {}
|
||||
~VAProvider() = default;
|
||||
|
||||
const char* get(const char* format, va_list ap)
|
||||
{
|
||||
@ -25,7 +25,7 @@ namespace Utils
|
||||
|
||||
while (true)
|
||||
{
|
||||
int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
const auto res = _vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
if (res > 0) break; // Success
|
||||
if (res == 0) return ""; // Error
|
||||
|
||||
@ -75,12 +75,13 @@ namespace Utils
|
||||
const char *VA(const char *fmt, ...);
|
||||
|
||||
int IsSpace(int c);
|
||||
std::string ToLower(std::string input);
|
||||
std::string ToUpper(std::string input);
|
||||
bool EndsWith(const std::string& haystack, const std::string& needle);
|
||||
std::string ToLower(std::string text);
|
||||
std::string ToUpper(std::string text);
|
||||
std::vector<std::string> Split(const std::string& str, const char delim);
|
||||
void Replace(std::string& string, const std::string& find, const std::string& replace);
|
||||
bool StartsWith(const std::string& haystack, const std::string& needle);
|
||||
bool EndsWith(const std::string& haystack, const std::string& needle);
|
||||
|
||||
std::string& LTrim(std::string& str);
|
||||
std::string& RTrim(std::string& str);
|
||||
std::string& Trim(std::string& str);
|
||||
|
@ -123,10 +123,6 @@ namespace Utils
|
||||
|
||||
void SafeShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd)
|
||||
{
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Components::AntiCheat::LibUnlocker _;
|
||||
#endif
|
||||
|
||||
[=]()
|
||||
{
|
||||
__try
|
||||
|
Loading…
Reference in New Issue
Block a user