- Версия SA-MP
-
- Другая
описание: дает возможность подменить ваш фпс, который отображает сервер.
работает подменяя результат вызова функции GetDrunkLevel на нужное значение, благодаря которой сервер высчитывывает фпс.
активация: /fakefps <fps от 5 до 400> | Если ввести без аргумента - вернет все по стандарту
зачем? зайдя на бразильский дм сервер (blood dm) был удивлён тому, что они до сих пор ограничивают игроков в фпсе, мол ставьте 100 фпс и ниже или не играйте =DDD
по приколу решил сделать это, возможно кому-то понадобится, вроде как есть есть аналог в LUA, но мне нужно было принципиально ASI, без зависимостей, а также в плюс поддержка всех версий сампа
работает подменяя результат вызова функции GetDrunkLevel на нужное значение, благодаря которой сервер высчитывывает фпс.
активация: /fakefps <fps от 5 до 400> | Если ввести без аргумента - вернет все по стандарту
зачем? зайдя на бразильский дм сервер (blood dm) был удивлён тому, что они до сих пор ограничивают игроков в фпсе, мол ставьте 100 фпс и ниже или не играйте =DDD
по приколу решил сделать это, возможно кому-то понадобится, вроде как есть есть аналог в LUA, но мне нужно было принципиально ASI, без зависимостей, а также в плюс поддержка всех версий сампа
впадлу было делить что-то, накидал все в одном ваще)))))
C++:
#include <iostream>
#include <windows.h>
#include <thread>
#include <string>
#include <random>
#include "minhook/include/MinHook.h"
class c_offsets {
public:
c_offsets(std::uintptr_t base) {
call_drunk_level = base;
input = base; register_chat_command = base;
chat = base; add_chat_msg = base;
auto ver = _get_version(base);
switch (ver) {
case versions::r1:
call_drunk_level += 0x5B32;
input += 0x21A0E8;
register_chat_command += 0x65AD0;
chat += 0x21A0E4;
add_chat_msg += 0x645A0;
break;
case versions::r2:
call_drunk_level += 0x5B12;
input += 0x21A0F0;
register_chat_command += 0x65BA0;
chat += 0x21A0EC;
add_chat_msg += 0x64670;
break;
case versions::r3:
call_drunk_level += 0x5B52;
input += 0x26E8CC;
register_chat_command += 0x69000;
chat += 0x26E8C8;
add_chat_msg += 0x679F0;
break;
case versions::r4:
call_drunk_level += 0x5D36;
input += 0x26E9FC;
register_chat_command += 0x69730;
chat += 0x26E9F8;
add_chat_msg += 0x68130;
break;
case versions::r5:
call_drunk_level += 0x5D46;
input += 0x26EB84;
register_chat_command += 0x69770;
chat += 0x26EB80;
add_chat_msg += 0x68170;
break;
}
};
std::uintptr_t call_drunk_level{};
std::uintptr_t input{};
std::uintptr_t register_chat_command{};
std::uintptr_t chat{};
std::uintptr_t add_chat_msg{};
private:
enum versions {
r1,
r2,
r3,
r4,
r5,
unknown = -1
};
versions _get_version(std::uintptr_t base) {
auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
if (!dos_header) return versions::unknown;
auto nt_headers = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dos_header->e_lfanew);
auto ep = nt_headers->OptionalHeader.AddressOfEntryPoint;
switch (ep) {
case 0x31DF13: return versions::r1;
case 0x3195DD: return versions::r2;
case 0xCC490: return versions::r3; // r0
case 0xCC4D0: return versions::r3; // r1
case 0xCBCB0: return versions::r4;
case 0xCBC90: return versions::r5;
default: return versions::unknown;
}
}
};
class c_samp {
public:
using cmd_proc_t = void(__cdecl*)(const char*);
c_offsets* p_offsets;
c_samp(std::uintptr_t base) : p_offsets(new c_offsets(base)) {}
bool is_inited() {
_samp_input = *reinterpret_cast<c_samp_input**>(p_offsets->input);
_samp_chat = *reinterpret_cast<c_samp_chat**>(p_offsets->chat);
return _samp_input != nullptr && _samp_chat != nullptr;
}
void register_chat_command(const char* command, cmd_proc_t function) {
reinterpret_cast<void(__thiscall*)(c_samp_input*, const char*, cmd_proc_t)>
(p_offsets->register_chat_command)(_samp_input, command, function);
}
void add_chat_msg(std::uint32_t ulColor, const char* szText)
{
reinterpret_cast<void(__thiscall*)(c_samp_chat*, std::uint32_t, const char*)>
(p_offsets->add_chat_msg)(_samp_chat, ulColor, szText);
}
private:
class c_samp_input* _samp_input{};
class c_samp_chat* _samp_chat{};
};
class c_asi
{
public:
c_asi();
~c_asi();
private:
static std::uintptr_t _samp_base;
static c_samp* _samp;
static int _fps;
static std::uint32_t _last_update;
static std::uint32_t _drunk_level;
static void (*p_game_loop)();
static std::random_device _rd;
static std::mt19937 _gen;
static std::uniform_int_distribution<> _distrib;
static int calc_drunk_level(int v) {
if (_drunk_level <= _fps) _drunk_level = 2000;
_drunk_level = _drunk_level - v;
return _drunk_level * 256 + 184;
}
static void __cdecl game_loop() {
static bool inited = false;
if (!inited) {
if (!_samp_base) _samp_base = (std::uintptr_t)GetModuleHandleA("samp.dll");
if (_samp_base && !_samp) _samp = new c_samp(_samp_base);
if (_samp && _samp->is_inited()) {
_samp->register_chat_command("fakefps", &cmd_change_fps);
inited = true;
}
}
if (inited && _fps != 0) {
auto current_ticks = GetTickCount();
if (current_ticks - _last_update > 1000) {
_last_update = current_ticks;
auto new_val = calc_drunk_level(_fps);
auto res = write_memory(_samp->p_offsets->call_drunk_level, new_val);
}
}
p_game_loop();
}
static void msg(std::string s) {
s = "{ff7581}[FakeFps]{e6e6e6} " + s;
_samp->add_chat_msg(-1, s.c_str());
}
static void cmd_change_fps(const char* s) {
try {
std::uint32_t fps = std::stoi(s);
if (fps > 400) {
msg("Максимально 400 фпс");
return;
}
else if (fps < 5) {
msg("Минимально 5 фпс");
return;
}
_fps = fps;
msg("Фпс изменен на " + std::to_string(fps));
auto val = calc_drunk_level(fps);
write_memory(_samp->p_offsets->call_drunk_level, val);
}
catch (const std::invalid_argument& e) {
if (_fps != 0) {
msg("Используйте /fakefps <fps от 5 до 400> | Показатель fps возвращен");
write_memory(_samp->p_offsets->call_drunk_level, 170723816);
_fps = 0;
} else msg("Используйте /fakefps <fps от 5 до 400>");
};
}
static bool write_memory(uintptr_t adr, int val) {
DWORD old_protect;
if (VirtualProtect((LPVOID)adr, 4, PAGE_EXECUTE_READWRITE, &old_protect)) {
memcpy((LPVOID)adr, &val, 4);
VirtualProtect((LPVOID)adr, 4, old_protect, &old_protect);
return true;
}
return false;
}
}g_asi;
std::uintptr_t c_asi::_samp_base{};
c_samp* c_asi::_samp{};
std::uint32_t c_asi::_last_update{};
std::uint32_t c_asi::_drunk_level = 2000;
int c_asi::_fps{};
void (*c_asi::p_game_loop)() = nullptr;
std::random_device c_asi::_rd;
std::mt19937 c_asi::_gen(c_asi::_rd());
std::uniform_int_distribution<> c_asi::_distrib(-1, 1);
c_asi::c_asi()
{
MH_Initialize();
MH_CreateHook(reinterpret_cast<void*>(0x58A330), &game_loop, reinterpret_cast<void**>(&p_game_loop));
MH_EnableHook(reinterpret_cast<void*>(0x58A330));
_samp_base = (std::uintptr_t)GetModuleHandleA("samp.dll");
}
c_asi::~c_asi() {
MH_RemoveHook(reinterpret_cast<void*>(0x58A330));
write_memory(_samp->p_offsets->call_drunk_level, 170723816);
}