2021-09-07 00:40:37 +02:00
|
|
|
#include <std_include.hpp>
|
2021-08-30 04:58:10 +02:00
|
|
|
#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 "notifies.hpp"
|
2022-09-19 18:33:51 +02:00
|
|
|
#include "scripting.hpp"
|
2021-08-30 04:58:10 +02:00
|
|
|
|
|
|
|
#include <utils/hook.hpp>
|
|
|
|
|
|
|
|
namespace notifies
|
|
|
|
{
|
|
|
|
bool hook_enabled = true;
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2022-08-26 03:59:50 +02:00
|
|
|
struct gsc_hook_t
|
|
|
|
{
|
|
|
|
bool is_lua_hook{};
|
2023-05-24 17:05:01 +02:00
|
|
|
bool is_variable{};
|
2023-06-01 20:43:21 +02:00
|
|
|
const char* target_pos{};
|
|
|
|
size_t rel_source_pos{};
|
2022-08-26 03:59:50 +02:00
|
|
|
sol::protected_function lua_function;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unordered_map<const char*, gsc_hook_t> vm_execute_hooks;
|
2021-09-17 00:12:54 +02:00
|
|
|
utils::hook::detour scr_entity_damage_hook;
|
|
|
|
std::vector<sol::protected_function> entity_damage_callbacks;
|
|
|
|
|
2021-08-30 04:58:10 +02:00
|
|
|
char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END
|
2022-08-26 03:59:50 +02:00
|
|
|
const char* target_function = nullptr;
|
2023-05-24 17:05:01 +02:00
|
|
|
bool gsc_hooks_buffer[0x1400000]{};
|
2021-08-30 04:58:10 +02:00
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
scripting::entity local_id_to_entity(unsigned int local_id)
|
2021-08-30 04:58:10 +02:00
|
|
|
{
|
2023-05-15 19:45:35 +02:00
|
|
|
return game::scr_VarGlob->objectVariableValue[local_id].u.f.next;
|
2021-08-30 04:58:10 +02:00
|
|
|
}
|
|
|
|
|
2023-05-24 17:05:01 +02:00
|
|
|
char* get_program_buffer()
|
|
|
|
{
|
|
|
|
return *reinterpret_cast<char**>(0x14B5E0B78);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t get_program_buffer_offset(const char* pos)
|
|
|
|
{
|
|
|
|
const auto rel_offset = pos - get_program_buffer();
|
|
|
|
return rel_offset;
|
|
|
|
}
|
|
|
|
|
2021-08-30 04:58:10 +02:00
|
|
|
bool execute_vm_hook(const char* pos)
|
|
|
|
{
|
2023-05-24 17:05:01 +02:00
|
|
|
const auto program_buffer = get_program_buffer();
|
|
|
|
if (pos >= program_buffer && (pos - program_buffer < 0x1400000))
|
2021-08-30 04:58:10 +02:00
|
|
|
{
|
2023-05-24 17:05:01 +02:00
|
|
|
const auto rel_offset = pos - program_buffer;
|
|
|
|
if (!gsc_hooks_buffer[rel_offset])
|
|
|
|
{
|
2023-06-01 20:43:21 +02:00
|
|
|
hook_enabled = true;
|
2023-05-24 17:05:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
2021-08-30 04:58:10 +02:00
|
|
|
}
|
|
|
|
|
2023-05-24 17:05:01 +02:00
|
|
|
const auto iter = vm_execute_hooks.find(pos);
|
|
|
|
if (iter == vm_execute_hooks.end() || (!hook_enabled && !iter->second.is_variable))
|
2021-08-30 04:58:10 +02:00
|
|
|
{
|
|
|
|
hook_enabled = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
const auto& hook = iter->second;
|
2022-08-26 03:59:50 +02:00
|
|
|
if (hook.is_lua_hook)
|
|
|
|
{
|
|
|
|
const auto& function = hook.lua_function;
|
|
|
|
const auto state = function.lua_state();
|
2021-08-30 04:58:10 +02:00
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
const auto self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
|
2022-08-26 03:59:50 +02:00
|
|
|
std::vector<sol::lua_value> args;
|
2021-08-30 04:58:10 +02:00
|
|
|
|
2022-08-26 03:59:50 +02:00
|
|
|
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));
|
|
|
|
}
|
2021-08-30 04:58:10 +02:00
|
|
|
|
2022-08-26 03:59:50 +02:00
|
|
|
const auto result = function(self, sol::as_args(args));
|
|
|
|
scripting::lua::handle_error(result);
|
|
|
|
target_function = empty_function;
|
|
|
|
}
|
|
|
|
else
|
2021-08-30 04:58:10 +02:00
|
|
|
{
|
2022-08-26 03:59:50 +02:00
|
|
|
target_function = hook.target_pos;
|
2021-08-30 04:58:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
a.mov(dword_ptr(rbp, 0xA4), r15d);
|
|
|
|
|
2022-03-18 22:02:44 +01:00
|
|
|
a.jmp(0x1405C90B3);
|
2021-08-30 04:58:10 +02:00
|
|
|
|
|
|
|
a.bind(replace);
|
|
|
|
|
|
|
|
a.popad64();
|
2022-08-26 03:59:50 +02:00
|
|
|
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&target_function)));
|
|
|
|
a.mov(r14, rax);
|
2021-08-30 04:58:10 +02:00
|
|
|
a.jmp(end);
|
|
|
|
}
|
2021-09-17 00:12:54 +02:00
|
|
|
|
|
|
|
sol::lua_value convert_entity(lua_State* state, const game::gentity_s* ent)
|
|
|
|
{
|
|
|
|
if (!ent)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
const scripting::entity entity{game::scr_entref_t(ent->s.entityNum, 0)};
|
|
|
|
return scripting::lua::convert(state, entity);
|
2021-09-17 00:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string get_weapon_name(unsigned int weapon, bool isAlternate)
|
|
|
|
{
|
|
|
|
char output[1024] = {0};
|
|
|
|
game::BG_GetWeaponNameComplete(weapon, isAlternate, output, 1024);
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
sol::lua_value convert_vector(lua_State* state, const float* vec)
|
|
|
|
{
|
|
|
|
if (!vec)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
return scripting::lua::convert(state, scripting::vector(vec));
|
2021-09-17 00:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string convert_mod(const int meansOfDeath)
|
|
|
|
{
|
2022-03-18 22:02:44 +01:00
|
|
|
const auto value = reinterpret_cast<game::scr_string_t**>(0x140BF49B0)[meansOfDeath];
|
2023-05-15 19:45:35 +02:00
|
|
|
return game::SL_ConvertToString(*value);
|
2021-09-17 00:12:54 +02:00
|
|
|
}
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
void scr_entity_damage_stub(game::gentity_s* self, game::gentity_s* inflictor, game::gentity_s* attacker, const float* v_dir, const float* v_point,
|
|
|
|
int damage, int dflags, const unsigned int means_of_death, const unsigned int weapon, bool is_alternate,
|
|
|
|
unsigned int a11, const int hit_loc, unsigned int a13, unsigned int a14)
|
2021-09-17 00:12:54 +02:00
|
|
|
{
|
|
|
|
{
|
2023-05-15 19:45:35 +02:00
|
|
|
const std::string hit_loc_str = reinterpret_cast<const char**>(0x140BF4AA0)[hit_loc];
|
|
|
|
const auto mod_str = convert_mod(means_of_death);
|
|
|
|
const auto weapon_str = get_weapon_name(weapon, is_alternate);
|
2021-09-17 00:12:54 +02:00
|
|
|
|
|
|
|
for (const auto& callback : entity_damage_callbacks)
|
|
|
|
{
|
|
|
|
const auto state = callback.lua_state();
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
const auto self_ent = convert_entity(state, self);
|
|
|
|
const auto inflictor_ent = convert_entity(state, inflictor);
|
|
|
|
const auto attacker_ent = convert_entity(state, attacker);
|
|
|
|
const auto v_dir_vec = convert_vector(state, v_dir);
|
2021-09-17 00:12:54 +02:00
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
const auto result = callback(self_ent, inflictor_ent, attacker_ent, damage, mod_str, weapon_str, v_dir_vec, hit_loc_str);
|
2021-09-17 00:12:54 +02:00
|
|
|
scripting::lua::handle_error(result);
|
|
|
|
|
|
|
|
if (result.valid())
|
|
|
|
{
|
|
|
|
const auto value = result.get<sol::object>();
|
|
|
|
if (value.is<int>())
|
|
|
|
{
|
|
|
|
damage = value.as<int>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (damage == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
scr_entity_damage_hook.invoke<void>(self, inflictor, attacker, v_dir, v_point, damage, dflags,
|
|
|
|
means_of_death, weapon, is_alternate, a11, hit_loc, a13, a14);
|
2021-09-17 00:12:54 +02:00
|
|
|
}
|
2023-05-24 17:05:01 +02:00
|
|
|
|
|
|
|
void set_hook(const char* pos, gsc_hook_t& hook)
|
|
|
|
{
|
2023-06-01 20:43:21 +02:00
|
|
|
if (!hook.is_variable)
|
|
|
|
{
|
|
|
|
const auto rel_pos = get_program_buffer_offset(pos);
|
|
|
|
gsc_hooks_buffer[rel_pos] = true;
|
|
|
|
hook.rel_source_pos = rel_pos;
|
|
|
|
}
|
|
|
|
|
2023-05-24 17:05:01 +02:00
|
|
|
vm_execute_hooks[pos] = hook;
|
|
|
|
}
|
2021-09-17 00:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void add_entity_damage_callback(const sol::protected_function& callback)
|
|
|
|
{
|
|
|
|
entity_damage_callbacks.push_back(callback);
|
2021-08-30 04:58:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void clear_callbacks()
|
|
|
|
{
|
2022-09-19 18:33:51 +02:00
|
|
|
for (auto i = vm_execute_hooks.begin(); i != vm_execute_hooks.end();)
|
|
|
|
{
|
|
|
|
if (i->second.is_lua_hook)
|
|
|
|
{
|
2023-06-01 20:43:21 +02:00
|
|
|
if (!i->second.is_variable)
|
|
|
|
{
|
|
|
|
gsc_hooks_buffer[i->second.rel_source_pos] = false;
|
|
|
|
}
|
|
|
|
|
2022-09-19 18:33:51 +02:00
|
|
|
i = vm_execute_hooks.erase(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-17 00:12:54 +02:00
|
|
|
entity_damage_callbacks.clear();
|
2021-08-30 04:58:10 +02:00
|
|
|
}
|
|
|
|
|
2023-05-24 17:05:01 +02:00
|
|
|
void set_lua_hook(const char* pos, const sol::protected_function& callback, bool is_variable)
|
2022-08-26 03:59:50 +02:00
|
|
|
{
|
2023-06-01 20:43:21 +02:00
|
|
|
gsc_hook_t hook{};
|
2022-08-26 03:59:50 +02:00
|
|
|
hook.is_lua_hook = true;
|
2023-05-24 17:05:01 +02:00
|
|
|
hook.is_variable = is_variable;
|
2022-08-26 03:59:50 +02:00
|
|
|
hook.lua_function = callback;
|
2023-05-24 17:05:01 +02:00
|
|
|
set_hook(pos, hook);
|
2022-08-26 03:59:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_gsc_hook(const char* source, const char* target)
|
|
|
|
{
|
2023-06-01 20:43:21 +02:00
|
|
|
gsc_hook_t hook{};
|
2022-08-26 03:59:50 +02:00
|
|
|
hook.is_lua_hook = false;
|
|
|
|
hook.target_pos = target;
|
2023-05-24 17:05:01 +02:00
|
|
|
set_hook(source, hook);
|
2022-08-26 03:59:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void clear_hook(const char* pos)
|
|
|
|
{
|
2023-05-24 17:05:01 +02:00
|
|
|
const auto rel_offset = get_program_buffer_offset(pos);
|
|
|
|
gsc_hooks_buffer[rel_offset] = false;
|
2022-08-26 03:59:50 +02:00
|
|
|
vm_execute_hooks.erase(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t get_hook_count()
|
|
|
|
{
|
|
|
|
return vm_execute_hooks.size();
|
|
|
|
}
|
|
|
|
|
2021-08-30 04:58:10 +02:00
|
|
|
class component final : public component_interface
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void post_unpack() override
|
|
|
|
{
|
2022-08-28 16:42:57 +02:00
|
|
|
utils::hook::jump(0x1405C90A5, utils::hook::assemble(vm_execute_stub), true);
|
2021-09-17 00:12:54 +02:00
|
|
|
|
2022-03-18 22:02:44 +01:00
|
|
|
scr_entity_damage_hook.create(0x1404BD2E0, scr_entity_damage_stub);
|
2022-09-19 18:33:51 +02:00
|
|
|
|
2022-11-13 20:03:10 +01:00
|
|
|
scripting::on_shutdown([](bool free_scripts, bool post_shutdown)
|
2022-09-19 18:33:51 +02:00
|
|
|
{
|
2022-11-13 20:03:10 +01:00
|
|
|
if (free_scripts && !post_shutdown)
|
2022-09-19 18:33:51 +02:00
|
|
|
{
|
2023-05-24 17:05:01 +02:00
|
|
|
std::memset(gsc_hooks_buffer, 0, sizeof(gsc_hooks_buffer));
|
2022-09-19 18:33:51 +02:00
|
|
|
vm_execute_hooks.clear();
|
|
|
|
}
|
|
|
|
});
|
2021-08-30 04:58:10 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-05-15 19:45:35 +02:00
|
|
|
REGISTER_COMPONENT(notifies::component)
|