2022-03-01 17:08:43 -05:00
|
|
|
#include <std_include.hpp>
|
|
|
|
#include "loader/component_loader.hpp"
|
|
|
|
#include "scheduler.hpp"
|
|
|
|
|
|
|
|
#include "game/scripting/entity.hpp"
|
|
|
|
#include "game/scripting/execution.hpp"
|
|
|
|
#include "game/scripting/lua/value_conversion.hpp"
|
|
|
|
#include "game/scripting/lua/error.hpp"
|
|
|
|
|
|
|
|
#include <utils/hook.hpp>
|
|
|
|
|
|
|
|
#include "logfile.hpp"
|
|
|
|
|
|
|
|
namespace logfile
|
|
|
|
{
|
|
|
|
std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
utils::hook::detour scr_player_killed_hook;
|
|
|
|
utils::hook::detour scr_player_damage_hook;
|
|
|
|
|
|
|
|
std::vector<sol::protected_function> player_killed_callbacks;
|
|
|
|
std::vector<sol::protected_function> player_damage_callbacks;
|
|
|
|
|
|
|
|
utils::hook::detour vm_execute_hook;
|
2022-03-02 20:09:13 -05:00
|
|
|
char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END
|
2022-03-01 17:08:43 -05:00
|
|
|
bool hook_enabled = true;
|
|
|
|
|
|
|
|
sol::lua_value convert_entity(lua_State* state, const game::mp::gentity_s* ent)
|
|
|
|
{
|
|
|
|
if (!ent)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const scripting::entity player{game::Scr_GetEntityId(ent->s.entityNum, 0)};
|
2022-03-01 17:08:43 -05:00
|
|
|
return scripting::lua::convert(state, player);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string get_weapon_name(unsigned int weapon, bool isAlternate)
|
|
|
|
{
|
2022-03-02 20:09:13 -05:00
|
|
|
char output[1024] = {0};
|
2022-03-01 17:08:43 -05:00
|
|
|
game::BG_GetWeaponNameComplete(weapon, isAlternate, output, 1024);
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
sol::lua_value convert_vector(lua_State* state, const float* vec)
|
|
|
|
{
|
|
|
|
if (!vec)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto vec_ = scripting::vector(vec);
|
|
|
|
return scripting::lua::convert(state, vec_);
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string convert_mod(const int meansOfDeath)
|
|
|
|
{
|
|
|
|
const auto value = reinterpret_cast<game::scr_string_t**>(0x140FEC3F0)[meansOfDeath];
|
|
|
|
const auto string = game::SL_ConvertToString(*value);
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
void scr_player_killed_stub(game::mp::gentity_s* self, const game::mp::gentity_s* inflictor,
|
|
|
|
game::mp::gentity_s* attacker, int damage, const int meansOfDeath, const unsigned int weapon,
|
|
|
|
const bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration)
|
2022-03-01 17:08:43 -05:00
|
|
|
{
|
|
|
|
{
|
2022-03-02 20:09:13 -05:00
|
|
|
const std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc];
|
|
|
|
const auto mod_ = convert_mod(meansOfDeath);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto weapon_ = get_weapon_name(weapon, isAlternate);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
for (const auto& callback : player_killed_callbacks)
|
|
|
|
{
|
|
|
|
const auto state = callback.lua_state();
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto self_ = convert_entity(state, self);
|
|
|
|
const auto inflictor_ = convert_entity(state, inflictor);
|
|
|
|
const auto attacker_ = convert_entity(state, attacker);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto dir = convert_vector(state, vDir);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto result = callback(self_, inflictor_, attacker_, damage,
|
|
|
|
mod_, weapon_, dir, hitloc, psTimeOffset, deathAnimDuration);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
scripting::lua::handle_error(result);
|
|
|
|
|
|
|
|
if (result.valid() && result.get_type() == sol::type::number)
|
|
|
|
{
|
|
|
|
damage = result.get<int>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (damage == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
scr_player_killed_hook.invoke<void>(self, inflictor, attacker, damage, meansOfDeath,
|
|
|
|
weapon, isAlternate, vDir, hitLoc, psTimeOffset, deathAnimDuration);
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
void scr_player_damage_stub(game::mp::gentity_s* self, const game::mp::gentity_s* inflictor,
|
|
|
|
game::mp::gentity_s* attacker, int damage, int dflags, const int meansOfDeath,
|
|
|
|
const unsigned int weapon, const bool isAlternate, const float* vPoint,
|
|
|
|
const float* vDir, const unsigned int hitLoc, const int timeOffset)
|
2022-03-01 17:08:43 -05:00
|
|
|
{
|
|
|
|
{
|
2022-03-02 20:09:13 -05:00
|
|
|
const std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc];
|
|
|
|
const auto mod_ = convert_mod(meansOfDeath);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto weapon_ = get_weapon_name(weapon, isAlternate);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
for (const auto& callback : player_damage_callbacks)
|
|
|
|
{
|
|
|
|
const auto state = callback.lua_state();
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto self_ = convert_entity(state, self);
|
|
|
|
const auto inflictor_ = convert_entity(state, inflictor);
|
|
|
|
const auto attacker_ = convert_entity(state, attacker);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto point = convert_vector(state, vPoint);
|
|
|
|
const auto dir = convert_vector(state, vDir);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
const auto result = callback(self_, inflictor_, attacker_,
|
|
|
|
damage, dflags, mod_, weapon_, point, dir, hitloc);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
scripting::lua::handle_error(result);
|
|
|
|
|
|
|
|
if (result.valid() && result.get_type() == sol::type::number)
|
|
|
|
{
|
|
|
|
damage = result.get<int>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (damage == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
scr_player_damage_hook.invoke<void>(self, inflictor, attacker, damage, dflags,
|
|
|
|
meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset);
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void client_command_stub(const int clientNum)
|
|
|
|
{
|
|
|
|
auto self = &game::mp::g_entities[clientNum];
|
2022-03-02 20:09:13 -05:00
|
|
|
char cmd[1024] = {0};
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
|
|
|
|
|
|
|
if (cmd == "say"s || cmd == "say_team"s)
|
|
|
|
{
|
|
|
|
auto hidden = false;
|
|
|
|
std::string message(game::ConcatArgs(1));
|
|
|
|
|
|
|
|
hidden = message[1] == '/';
|
|
|
|
message.erase(0, hidden ? 2 : 1);
|
|
|
|
|
|
|
|
scheduler::once([cmd, message, self]()
|
2022-03-02 20:09:13 -05:00
|
|
|
{
|
|
|
|
const scripting::entity level{*game::levelEntityId};
|
|
|
|
const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)};
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
scripting::notify(level, cmd, {player, message});
|
|
|
|
scripting::notify(player, cmd, {message});
|
|
|
|
}, scheduler::pipeline::server);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
if (hidden)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClientCommand
|
2022-03-02 20:09:13 -05:00
|
|
|
return utils::hook::invoke<void>(0x140336000, clientNum);
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void g_shutdown_game_stub(const int freeScripts)
|
|
|
|
{
|
|
|
|
{
|
2022-03-02 20:09:13 -05:00
|
|
|
const scripting::entity level{*game::levelEntityId};
|
|
|
|
scripting::notify(level, "shutdownGame_called", {1});
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// G_ShutdownGame
|
2022-03-02 20:09:13 -05:00
|
|
|
return utils::hook::invoke<void>(0x140345A60, freeScripts);
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int local_id_to_entity(unsigned int local_id)
|
|
|
|
{
|
|
|
|
const auto variable = game::scr_VarGlob->objectVariableValue[local_id];
|
|
|
|
return variable.u.f.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool execute_vm_hook(const char* pos)
|
|
|
|
{
|
|
|
|
if (vm_execute_hooks.find(pos) == vm_execute_hooks.end())
|
|
|
|
{
|
|
|
|
hook_enabled = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hook_enabled && pos > reinterpret_cast<char*>(vm_execute_hooks.size()))
|
|
|
|
{
|
|
|
|
hook_enabled = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-04 19:30:24 -04:00
|
|
|
const auto& hook = vm_execute_hooks[pos];
|
2022-03-01 17:08:43 -05:00
|
|
|
const auto state = hook.lua_state();
|
|
|
|
|
|
|
|
const scripting::entity self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
|
|
|
|
|
|
|
|
std::vector<sol::lua_value> args;
|
|
|
|
|
|
|
|
const auto top = game::scr_function_stack->top;
|
|
|
|
|
|
|
|
for (auto* value = top; value->type != game::SCRIPT_END; --value)
|
|
|
|
{
|
|
|
|
args.push_back(scripting::lua::convert(state, *value));
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto result = hook(self, sol::as_args(args));
|
|
|
|
scripting::lua::handle_error(result);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vm_execute_stub(utils::hook::assembler& a)
|
|
|
|
{
|
|
|
|
const auto replace = a.newLabel();
|
|
|
|
const auto end = a.newLabel();
|
|
|
|
|
|
|
|
a.pushad64();
|
|
|
|
|
|
|
|
a.mov(rcx, r14);
|
|
|
|
a.call_aligned(execute_vm_hook);
|
|
|
|
|
|
|
|
a.cmp(al, 0);
|
|
|
|
a.jne(replace);
|
|
|
|
|
|
|
|
a.popad64();
|
|
|
|
a.jmp(end);
|
|
|
|
|
|
|
|
a.bind(end);
|
|
|
|
|
|
|
|
a.movzx(r15d, byte_ptr(r14));
|
|
|
|
a.inc(r14);
|
2022-03-02 20:09:13 -05:00
|
|
|
a.mov(dword_ptr(rbp, 0xA4), r15d);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-02 20:09:13 -05:00
|
|
|
a.jmp(SELECT_VALUE(0x140376663, 0x140444653));
|
2022-03-01 17:08:43 -05:00
|
|
|
|
|
|
|
a.bind(replace);
|
|
|
|
|
|
|
|
a.popad64();
|
|
|
|
a.mov(r14, reinterpret_cast<char*>(empty_function));
|
|
|
|
a.jmp(end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_player_damage_callback(const sol::protected_function& callback)
|
|
|
|
{
|
|
|
|
player_damage_callbacks.push_back(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_player_killed_callback(const sol::protected_function& callback)
|
|
|
|
{
|
|
|
|
player_killed_callbacks.push_back(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear_callbacks()
|
|
|
|
{
|
|
|
|
player_damage_callbacks.clear();
|
|
|
|
player_killed_callbacks.clear();
|
|
|
|
vm_execute_hooks.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void enable_vm_execute_hook()
|
|
|
|
{
|
|
|
|
hook_enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void disable_vm_execute_hook()
|
|
|
|
{
|
|
|
|
hook_enabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
class component final : public component_interface
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void post_unpack() override
|
|
|
|
{
|
2022-04-08 12:24:35 -04:00
|
|
|
utils::hook::jump(SELECT_VALUE(0x140376655, 0x140444645), utils::hook::assemble(vm_execute_stub), true);
|
|
|
|
|
2022-03-04 14:55:45 -05:00
|
|
|
if (game::environment::is_sp())
|
2022-03-01 17:08:43 -05:00
|
|
|
{
|
2022-03-04 14:55:45 -05:00
|
|
|
return;
|
|
|
|
}
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-04 14:55:45 -05:00
|
|
|
utils::hook::call(0x14048191D, client_command_stub);
|
2022-03-01 17:08:43 -05:00
|
|
|
|
2022-03-04 14:55:45 -05:00
|
|
|
scr_player_damage_hook.create(0x14037DC50, scr_player_damage_stub);
|
|
|
|
scr_player_killed_hook.create(0x14037DF30, scr_player_killed_stub);
|
|
|
|
|
|
|
|
utils::hook::call(0x140484EC0, g_shutdown_game_stub);
|
|
|
|
utils::hook::call(0x1404853C1, g_shutdown_game_stub);
|
2022-03-01 17:08:43 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-05-17 10:56:26 -04:00
|
|
|
//REGISTER_COMPONENT(logfile::component)
|